Source code for pyatlan.model.custom_metadata
from __future__ import annotations
from collections import UserDict
from typing import TYPE_CHECKING, Any, Dict, Optional, Set
from pydantic.v1 import PrivateAttr
from pyatlan.errors import NotFoundError
from pyatlan.model.constants import DELETED_, DELETED_SENTINEL
from pyatlan.model.core import AtlanObject
if TYPE_CHECKING:
from pyatlan.client.atlan import AtlanClient
[docs]
class CustomMetadataDict(UserDict):
"""This class allows the manipulation of a set of custom metadata attributes using the human-readable names."""
_sentinel: Optional["CustomMetadataDict"] = 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()
cls._sentinel = obj
return obj
@property
def attribute_names(self) -> Set[str]:
return self._names
def __init__(self, client: AtlanClient, name: str):
"""Inits CustomMetadataDict with a string containing the human-readable name of a set of custom metadata"""
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)
}
[docs]
@classmethod
def get_deleted_sentinel(cls) -> "CustomMetadataDict":
"""Will return an CustomMetadataDict that is a sentinel object to represent deleted custom meta data."""
if cls._sentinel is not None:
return cls._sentinel
return cls.__new__(
cls, DELETED_SENTINEL
) # Because __new__ is being invoked directly __init__ won't be invoked
@property
def modified(self):
"""Returns a boolean indicating whether the set has been modified from its initial values"""
return self._modified
def __setitem__(self, key: str, value):
"""Set the value of a property of the custom metadata set using the human-readable name as the key.
The name will be validated to ensure that it's valid for this custom metadata set
"""
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 the value of a property of the custom metadata set using the human-readable name as the key.
The name will be validated to ensure that it's valid for this custom metadata set
"""
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]
[docs]
def clear_all(self):
"""This method will set all the properties available explicitly to None"""
for attribute_name in self._names:
self.data[attribute_name] = None
self._modified = True
[docs]
def clear_unset(self):
"""This method will 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
[docs]
def is_set(self, key: str):
"""Returns a boolean indicating whether the given property has been set in the metadata set. The key
will be validated to ensure that it's a valid property name for this 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]:
"""Returns a dict containing the metadata set with the human-readable set name and property 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()
}
[docs]
class CustomMetadataProxy:
def __init__(
self,
client: AtlanClient,
business_attributes: Optional[Dict[str, Any]],
):
self._client = client
self._metadata: Optional[Dict[str, CustomMetadataDict]] = 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
)
# Only set active custom metadata attributes
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:
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):
if self._metadata is None:
self._metadata = {}
self._metadata[custom_metadata._name] = custom_metadata
self._modified = True
@property
def modified(self) -> bool:
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) -> Optional[Dict[str, Any]]:
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
[docs]
class CustomMetadataRequest(AtlanObject):
__root__: Dict[str, Any]
_set_id: str = PrivateAttr()
@classmethod
def create(cls, custom_metadata_dict: CustomMetadataDict):
ret_val = cls(__root__=custom_metadata_dict.business_attributes)
ret_val._set_id = (
custom_metadata_dict._client.custom_metadata_cache.get_id_for_name(
custom_metadata_dict._name
)
)
return ret_val
@property
def custom_metadata_set_id(self):
return self._set_id