"""Resource helper class."""
from __future__ import annotations
from typing import TYPE_CHECKING, ClassVar
from foundry_dev_tools.clients.compass import get_decoration
from foundry_dev_tools.errors.compass import WrongResourceTypeError
if TYPE_CHECKING:
import sys
from foundry_dev_tools.config.context import FoundryContext
from foundry_dev_tools.utils import api_types
if sys.version_info < (3, 11):
from typing_extensions import Self
else:
from typing import Self
[docs]
class Resource:
"""Helper class for Foundry Resources."""
_context: FoundryContext
# always included (at least what I can see)
rid: api_types.Rid
name: str
path: api_types.FoundryPath # not optional for this class, normally only included with extra decorations
created: api_types.Attribution | None
modified: api_types.Attribution | None
last_modified: float | None
directly_trashed: bool
is_autosave: bool
is_hidden: bool
operations: set[str] | None
url_variables: dict[str, str]
# included with extra decoration
description: str | None = None
favorite: bool | None = None
branches: set[api_types.CompassBranch] | None = None
default_branch: api_types.CompassBranch | None = None
default_branch_with_markings: api_types.BranchWithMarkings | None = None
branches_count: int | None = None
has_branches: bool | None = None
has_multiple_branches: bool | None = None
backed_object_types: set[api_types.BackedObjectTypeInfo] | None = None
long_description: str | None = None
in_trash: bool | None = None
deprecation: api_types.Deprecation | None = None
collections: set[str] | None = None # Assuming RID is a string
named_collections: set[api_types.NamedResourceIdentifier] | None = None
tags: set[str] | None = None # Assuming RID is a string
named_tags: set[api_types.NamedResourceIdentifier] | None = None
alias: str | None = None
collaborators: set[api_types.Attribution] | None = None
named_ancestors: list[api_types.NamedResourceIdentifier] | None = None
markings: set[api_types.MarkingInfo] | None = None
project_access_markings: set[api_types.MarkingInfo] | None = None
linked_items: set[api_types.LinkedItem] | None = None
contact_information: api_types.ContactInformation | None = None
classification: api_types.Classification | None = None
disable_inherited_permissions: api_types.DisableInheritedPermissionsType | None = None
propagate_permissions: bool | None = None
resource_level_role_grants_allowed: bool | None = None
__created__: bool | None = None
_default_decorations: ClassVar[api_types.ResourceDecorationSet] = {"inTrash", "path"}
_decoration: api_types.ResourceDecorationSet
rid_start: ClassVar[str] = ""
[docs]
def __init__(self, *args, **kwargs):
"""Not intended to be initialized directly. Use :py:meth:`Resource.from_rid` or :py:meth:`Resource.from_path` instead.""" # noqa: E501
self._from_json(*args, **kwargs)
def _from_json(
self,
rid: api_types.Rid,
name: str,
path: api_types.FoundryPath,
directlyTrashed: bool, # noqa: N803
isAutosave: bool, # noqa: N803
isHidden: bool, # noqa: N803
created: api_types.Attribution | None,
modified: api_types.Attribution | None,
lastModified: float | None, # noqa: N803
operations: set[str] | None,
urlVariables: dict[str, str], # noqa: N803
description: str | None = None,
favorite: bool | None = None,
branches: set[api_types.CompassBranch] | None = None,
defaultBranch: api_types.CompassBranch | None = None, # noqa: N803
defaultBranchWithMarkings: api_types.BranchWithMarkings | None = None, # noqa: N803
branchesCount: int | None = None, # noqa: N803
hasBranches: bool | None = None, # noqa: N803
hasMultipleBranches: bool | None = None, # noqa: N803
backedObjectTypes: set[api_types.BackedObjectTypeInfo] | None = None, # noqa: N803
longDescription: str | None = None, # noqa: N803
inTrash: bool | None = None, # noqa: N803
deprecation: api_types.Deprecation | None = None,
collections: set[api_types.Rid] | None = None,
namedCollections: set[api_types.NamedResourceIdentifier] | None = None, # noqa: N803
tags: set[api_types.Rid] | None = None,
namedTags: set[api_types.NamedResourceIdentifier] | None = None, # noqa: N803
alias: str | None = None,
collaborators: set[api_types.Attribution] | None = None,
namedAncestors: list[api_types.NamedResourceIdentifier] | None = None, # noqa: N803
markings: set[api_types.MarkingInfo] | None = None,
projectAccessMarkings: set[api_types.MarkingInfo] | None = None, # noqa: N803
linkedItems: set[api_types.LinkedItem] | None = None, # noqa: N803
contactInformation: api_types.ContactInformation | None = None, # noqa: N803
classification: api_types.Classification | None = None,
disableInheritedPermissions: api_types.DisableInheritedPermissionsType | None = None, # noqa: N803
propagatePermissions: bool | None = None, # noqa: N803
resourceLevelRoleGrantsAllowed: bool | None = None, # noqa: N803
**kwargs,
):
self.rid = rid
self.name = name
self.path = path
self.created = created
self.modified = modified
self.last_modified = lastModified
self.directly_trashed = directlyTrashed
self.is_autosave = isAutosave
self.is_hidden = isHidden
self.operations = operations
self.url_variables = urlVariables
self.description = description
self.favorite = favorite
self.branches = branches
self.default_branch = defaultBranch
self.default_branch_with_markings = defaultBranchWithMarkings
self.branches_count = branchesCount
self.has_branches = hasBranches
self.has_multiple_branches = hasMultipleBranches
self.backed_object_types = backedObjectTypes
self.long_description = longDescription
self.in_trash = inTrash
self.deprecation = deprecation
self.collections = collections
self.named_collections = namedCollections
self.tags = tags
self.named_tags = namedTags
self.alias = alias
self.collaborators = collaborators
self.named_ancestors = namedAncestors
self.markings = markings
self.project_access_markings = projectAccessMarkings
self.linked_items = linkedItems
self.contact_information = contactInformation
self.classification = classification
self.disable_inherited_permissions = disableInheritedPermissions
self.propagate_permissions = propagatePermissions
self.resource_level_role_grants_allowed = resourceLevelRoleGrantsAllowed
self._kwargs = kwargs
@classmethod
def _create_class(
cls,
ctx: FoundryContext,
resource_json: dict,
decoration: api_types.ResourceDecorationSet,
) -> Self:
if cls is Resource:
for _rid_start, rid_cls in RID_CLASS_REGISTRY.items():
if resource_json["rid"].startswith(_rid_start):
cls = rid_cls
break
elif not resource_json["rid"].startswith(cls.rid_start):
raise WrongResourceTypeError(resource_json["rid"], resource_json["path"], cls)
instance = cls.__new__(cls)
instance.__created__ = False
instance._context = ctx # noqa: SLF001
instance._decoration = decoration # noqa: SLF001
cls.__init__(instance, **resource_json)
return instance
[docs]
@classmethod
def from_path(
cls,
context: FoundryContext,
path: api_types.FoundryPath,
/,
*,
decoration: api_types.ResourceDecorationSetAll | None = None,
**_kwargs,
) -> Self:
"""Returns resource at path.
Args:
context: the foundry context for the resource
path: the path where the resource is located on foundry
decoration: extra decoration for this resource
**_kwargs: not used in base class
"""
_decoration = get_decoration(decoration, False)
_decoration = cls._default_decorations if _decoration is None else cls._default_decorations.union(_decoration)
resource_json = context.compass.api_get_resource_by_path(
path,
decoration=_decoration,
).json()
return cls._create_class(context, resource_json=resource_json, decoration=_decoration)
[docs]
@classmethod
def from_rid(
cls,
context: FoundryContext,
rid: api_types.Rid,
/,
*,
decoration: api_types.ResourceDecorationSetAll | None = None,
**_kwargs,
) -> Self:
"""Returns resource from rid.
Args:
context: the foundry context for the resource
rid: the rid of the resource on foundry
decoration: extra decoration for this resource
**_kwargs: not used in base class
"""
_decoration = get_decoration(decoration, False)
_decoration = cls._default_decorations if _decoration is None else cls._default_decorations.union(_decoration)
resource_json = context.compass.api_get_resource(
rid,
decoration=_decoration,
).json()
return cls._create_class(context, resource_json=resource_json, decoration=_decoration)
[docs]
def add_to_trash(self) -> Self:
"""Adds resource to trash."""
self._context.compass.api_add_to_trash({self.rid})
return self.sync()
[docs]
def restore(self) -> Self:
"""Restores resource from trash."""
self._context.compass.api_restore({self.rid})
return self.sync()
[docs]
def delete_permanently(self) -> Self:
"""Deletes resource permanently."""
self._context.compass.api_delete_permanently({self.rid})
return self
[docs]
def sync(self) -> Self:
"""Fetches the attributes again."""
resource_json = self._context.compass.api_get_resource(self.rid, decoration=self._decoration).json()
self._from_json(**resource_json)
return self
def _get_repr_dict(self) -> dict:
d = {k: v for k, v in self.__dict__.items() if v is not None and not k.startswith("_")}
if c := d.get("created"):
d["created"] = c.get("time")
if c := d.get("modified"):
d["modified"] = c.get("time")
del d["operations"]
del d["url_variables"]
del d["last_modified"] # already as a formatted date in "modified"
del d["name"] # already visible in the "path"
return d
def __repr__(self):
return f"<{self.__class__.__name__}({self._get_repr_dict()!s})>"
RID_CLASS_REGISTRY: dict[str, type[Resource]] = {}