Source code for standardfile.item
# Copyright (c) 2018 Stephen Bunn <stephen@bunn.io>
# MIT License <https://opensource.org/licenses/MIT>
import enum
from typing import Any, Generic, TypeVar
import attr
import arrow
T_String = TypeVar("String")
T_Item = TypeVar("Item")
[docs]@attr.s
class String(Generic[T_String]):
"""Defines a Standard File string.
"""
_supported_versions = ("002", "003")
version = attr.ib(type=str)
auth_hash = attr.ib(type=str, repr=False)
uuid = attr.ib(type=str)
iv = attr.ib(type=str, repr=False)
cipher_text = attr.ib(type=str, repr=False)
[docs] @classmethod
def is_valid(cls, string: str) -> bool:
"""Check if a given string is valid.
:param string: The string the check
:type string: str
:return: True if valid, otherwise False
:rtype: bool
"""
return (
isinstance(string, str)
and len(string) > 0
and any(string.startswith(version) for version in cls._supported_versions)
)
[docs] @classmethod
def from_string(cls, string: str) -> T_String:
"""Creates an instance from a string.
:param string: The string to create an instance from
:type string: str
:return: An instance of ``String``
:rtype: T_String
"""
if cls.is_valid(string):
if string.startswith("002") or string.startswith("003"):
return cls(*string.split(":"))
raise ValueError(f"unsupported string {string!r}")
[docs] def to_string(self) -> str:
"""Writes string out to a dictionary.
:return: The resulting string
:rtype: str
"""
if self.version in ("002", "003"):
return ":".join(
[self.version, self.auth_hash, self.uuid, self.iv, self.cipher_text]
)
raise ValueError(f"unsupported string version {self.version!r}")
[docs]@attr.s
class Item(Generic[T_Item]):
"""Defines a Standard File item.
Defined by StandardFile at `<https://standardfile.org/#items>`__.
"""
uuid = attr.ib(type=str)
content = attr.ib(type=str, repr=False)
content_type = attr.ib(type=str, repr=False)
enc_item_key = attr.ib(type=str, repr=False)
deleted = attr.ib(type=bool, repr=False)
created_at = attr.ib(type=str, converter=arrow.get, repr=False)
updated_at = attr.ib(type=str, converter=arrow.get, repr=False)
auth_hash = attr.ib(type=str, default=None, repr=False)
def __attrs_post_init__(self):
"""Initializes class attributes after initial initialization.
"""
self.enc_item_key = (
String.from_string(self.enc_item_key)
if String.is_valid(self.enc_item_key)
else self.enc_item_key
)
self.content = (
String.from_string(self.content)
if String.is_valid(self.content) and isinstance(self.enc_item_key, String)
else self.content
)
[docs] @classmethod
def from_dict(cls, item_dict: dict) -> T_Item:
"""Creates an instance from a dictionary.
:param item_dict: The dictionary to create an item from
:type item_dict: dict
:return: An instance of ``Item``
:rtype: T_Item
"""
if isinstance(item_dict, dict) and len(item_dict) > 0:
return cls(**item_dict)
[docs] def to_dict(self) -> dict:
"""Writes item out to a dictionary.
:return: The resulting dictionary
:rtype: dict
"""
retn = attr.asdict(self)
retn.update(
dict(
created_at=self.created_at.isoformat(),
updated_at=self.updated_at.isoformat(),
)
)
if isinstance(self.content, String):
retn["content"] = self.content.to_string()
if isinstance(self.enc_item_key, String):
retn["enc_item_key"] = self.enc_item_key.to_string()
return retn