allspice.baseapiobject

  1from __future__ import annotations
  2
  3from typing import TYPE_CHECKING, Any, ClassVar, Mapping, Optional
  4
  5try:
  6    from typing_extensions import Self
  7except ImportError:
  8    from typing import Self
  9
 10if TYPE_CHECKING:
 11    from allspice.allspice import AllSpice
 12
 13from .exceptions import MissingEqualityImplementation, ObjectIsInvalid, RawRequestEndpointMissing
 14
 15
 16class ReadonlyApiObject:
 17    def __init__(self, allspice_client):
 18        self.allspice_client = allspice_client
 19        self.deleted = False  # set if .delete was called, so that an exception is risen
 20
 21    def __str__(self) -> str:
 22        return "AllSpiceObject (%s):" % (type(self))
 23
 24    def __eq__(self, other) -> bool:
 25        """Compare only fields that are part of the gitea-data identity"""
 26        raise MissingEqualityImplementation()
 27
 28    def __hash__(self) -> int:
 29        """Hash only fields that are part of the gitea-data identity"""
 30        raise MissingEqualityImplementation()
 31
 32    _fields_to_parsers: ClassVar[dict] = {}
 33
 34    # TODO: This should probably be made an abstract function as all children
 35    # redefine it.
 36    @classmethod
 37    def request(cls, allspice_client: AllSpice) -> Self:
 38        # This never would've worked, so maybe we should remove this function
 39        # outright.
 40        return cls._request(allspice_client)
 41
 42    @classmethod
 43    def _request(cls, allspice_client: AllSpice, args: Mapping) -> Self:
 44        result = cls._get_gitea_api_object(allspice_client, args)
 45        api_object = cls.parse_response(allspice_client, result)
 46        return api_object
 47
 48    @classmethod
 49    def _get_gitea_api_object(cls, allspice_client: AllSpice, args: Mapping) -> Mapping:
 50        """Retrieving an object always as GET_API_OBJECT"""
 51        if hasattr(cls, "API_OBJECT"):
 52            raw_request_endpoint = getattr(cls, "API_OBJECT")
 53            return allspice_client.requests_get(raw_request_endpoint.format(**args))
 54        else:
 55            raise RawRequestEndpointMissing()
 56
 57    @classmethod
 58    def parse_response(cls, allspice_client: AllSpice, result: Mapping) -> Self:
 59        # allspice_client.logger.debug("Found api object of type %s (id: %s)" % (type(cls), id))
 60        api_object = cls(allspice_client)
 61        cls._initialize(allspice_client, api_object, result)
 62        return api_object
 63
 64    @classmethod
 65    def _initialize(cls, allspice_client: AllSpice, api_object: Self, result: Mapping):
 66        for name, value in result.items():
 67            if name in cls._fields_to_parsers and value is not None:
 68                parse_func = cls._fields_to_parsers[name]
 69                value = parse_func(allspice_client, value)
 70            cls._add_read_property(name, value, api_object)
 71        # add all patchable fields missing in the request to be writable
 72        for name in cls._fields_to_parsers.keys():
 73            if not hasattr(api_object, name):
 74                cls._add_read_property(name, None, api_object)
 75
 76    @classmethod
 77    def _add_read_property(cls, name: str, value: Any, api_object: ReadonlyApiObject):
 78        if not hasattr(api_object, name):
 79            setattr(api_object, "_" + name, value)
 80            prop = property((lambda n: lambda self: self._get_var(n))(name))
 81            setattr(cls, name, prop)
 82        else:
 83            raise AttributeError(f"Attribute {name} already exists on api object.")
 84
 85    def _get_var(self, name: str) -> Any:
 86        if self.deleted:
 87            raise ObjectIsInvalid()
 88        return getattr(self, "_" + name)
 89
 90
 91class ApiObject(ReadonlyApiObject):
 92    _patchable_fields: ClassVar[set[str]] = set()
 93
 94    def __init__(self, allspice_client: AllSpice):
 95        super().__init__(allspice_client)
 96        self._dirty_fields = set()
 97
 98    def _commit(self, route_fields: dict, dirty_fields: Optional[Mapping] = None):
 99        if self.deleted:
100            raise ObjectIsInvalid()
101        if not hasattr(self, "API_OBJECT"):
102            raise RawRequestEndpointMissing()
103
104        raw_request_endpoint = getattr(self, "API_OBJECT")
105
106        if dirty_fields is None:
107            dirty_fields = self.get_dirty_fields()
108
109        self.allspice_client.requests_patch(
110            raw_request_endpoint.format(**route_fields),
111            dirty_fields,
112        )
113        self._dirty_fields = set()
114
115    def commit(self):
116        raise NotImplementedError()
117
118    _parsers_to_fields: ClassVar[dict] = {}
119
120    def get_dirty_fields(self) -> dict[str, Any]:
121        dirty_fields_values = {}
122        for field in self._dirty_fields:
123            value = getattr(self, field)
124            if field in self._parsers_to_fields:
125                dirty_fields_values[field] = self._parsers_to_fields[field](value)
126            else:
127                dirty_fields_values[field] = value
128        return dirty_fields_values
129
130    @classmethod
131    def _initialize(cls, allspice_client: AllSpice, api_object: Self, result: Mapping):
132        super()._initialize(allspice_client, api_object, result)
133        for name in cls._patchable_fields:
134            cls._add_write_property(name, None, api_object)
135
136    @classmethod
137    def _add_write_property(cls, name: str, value: Any, api_object: Self):
138        if not hasattr(api_object, "_" + name):
139            setattr(api_object, "_" + name, value)
140        prop = property(
141            (lambda n: lambda self: self._get_var(n))(name),
142            (lambda n: lambda self, v: self.__set_var(n, v))(name),
143        )
144        setattr(cls, name, prop)
145
146    def __set_var(self, name: str, value: Any):
147        if self.deleted:
148            raise ObjectIsInvalid()
149        self._dirty_fields.add(name)
150        setattr(self, "_" + name, value)
class ReadonlyApiObject:
17class ReadonlyApiObject:
18    def __init__(self, allspice_client):
19        self.allspice_client = allspice_client
20        self.deleted = False  # set if .delete was called, so that an exception is risen
21
22    def __str__(self) -> str:
23        return "AllSpiceObject (%s):" % (type(self))
24
25    def __eq__(self, other) -> bool:
26        """Compare only fields that are part of the gitea-data identity"""
27        raise MissingEqualityImplementation()
28
29    def __hash__(self) -> int:
30        """Hash only fields that are part of the gitea-data identity"""
31        raise MissingEqualityImplementation()
32
33    _fields_to_parsers: ClassVar[dict] = {}
34
35    # TODO: This should probably be made an abstract function as all children
36    # redefine it.
37    @classmethod
38    def request(cls, allspice_client: AllSpice) -> Self:
39        # This never would've worked, so maybe we should remove this function
40        # outright.
41        return cls._request(allspice_client)
42
43    @classmethod
44    def _request(cls, allspice_client: AllSpice, args: Mapping) -> Self:
45        result = cls._get_gitea_api_object(allspice_client, args)
46        api_object = cls.parse_response(allspice_client, result)
47        return api_object
48
49    @classmethod
50    def _get_gitea_api_object(cls, allspice_client: AllSpice, args: Mapping) -> Mapping:
51        """Retrieving an object always as GET_API_OBJECT"""
52        if hasattr(cls, "API_OBJECT"):
53            raw_request_endpoint = getattr(cls, "API_OBJECT")
54            return allspice_client.requests_get(raw_request_endpoint.format(**args))
55        else:
56            raise RawRequestEndpointMissing()
57
58    @classmethod
59    def parse_response(cls, allspice_client: AllSpice, result: Mapping) -> Self:
60        # allspice_client.logger.debug("Found api object of type %s (id: %s)" % (type(cls), id))
61        api_object = cls(allspice_client)
62        cls._initialize(allspice_client, api_object, result)
63        return api_object
64
65    @classmethod
66    def _initialize(cls, allspice_client: AllSpice, api_object: Self, result: Mapping):
67        for name, value in result.items():
68            if name in cls._fields_to_parsers and value is not None:
69                parse_func = cls._fields_to_parsers[name]
70                value = parse_func(allspice_client, value)
71            cls._add_read_property(name, value, api_object)
72        # add all patchable fields missing in the request to be writable
73        for name in cls._fields_to_parsers.keys():
74            if not hasattr(api_object, name):
75                cls._add_read_property(name, None, api_object)
76
77    @classmethod
78    def _add_read_property(cls, name: str, value: Any, api_object: ReadonlyApiObject):
79        if not hasattr(api_object, name):
80            setattr(api_object, "_" + name, value)
81            prop = property((lambda n: lambda self: self._get_var(n))(name))
82            setattr(cls, name, prop)
83        else:
84            raise AttributeError(f"Attribute {name} already exists on api object.")
85
86    def _get_var(self, name: str) -> Any:
87        if self.deleted:
88            raise ObjectIsInvalid()
89        return getattr(self, "_" + name)
ReadonlyApiObject(allspice_client)
18    def __init__(self, allspice_client):
19        self.allspice_client = allspice_client
20        self.deleted = False  # set if .delete was called, so that an exception is risen
allspice_client
deleted
@classmethod
def request(cls, allspice_client: allspice.AllSpice) -> Self:
37    @classmethod
38    def request(cls, allspice_client: AllSpice) -> Self:
39        # This never would've worked, so maybe we should remove this function
40        # outright.
41        return cls._request(allspice_client)
@classmethod
def parse_response( cls, allspice_client: allspice.AllSpice, result: Mapping) -> Self:
58    @classmethod
59    def parse_response(cls, allspice_client: AllSpice, result: Mapping) -> Self:
60        # allspice_client.logger.debug("Found api object of type %s (id: %s)" % (type(cls), id))
61        api_object = cls(allspice_client)
62        cls._initialize(allspice_client, api_object, result)
63        return api_object
class ApiObject(ReadonlyApiObject):
 92class ApiObject(ReadonlyApiObject):
 93    _patchable_fields: ClassVar[set[str]] = set()
 94
 95    def __init__(self, allspice_client: AllSpice):
 96        super().__init__(allspice_client)
 97        self._dirty_fields = set()
 98
 99    def _commit(self, route_fields: dict, dirty_fields: Optional[Mapping] = None):
100        if self.deleted:
101            raise ObjectIsInvalid()
102        if not hasattr(self, "API_OBJECT"):
103            raise RawRequestEndpointMissing()
104
105        raw_request_endpoint = getattr(self, "API_OBJECT")
106
107        if dirty_fields is None:
108            dirty_fields = self.get_dirty_fields()
109
110        self.allspice_client.requests_patch(
111            raw_request_endpoint.format(**route_fields),
112            dirty_fields,
113        )
114        self._dirty_fields = set()
115
116    def commit(self):
117        raise NotImplementedError()
118
119    _parsers_to_fields: ClassVar[dict] = {}
120
121    def get_dirty_fields(self) -> dict[str, Any]:
122        dirty_fields_values = {}
123        for field in self._dirty_fields:
124            value = getattr(self, field)
125            if field in self._parsers_to_fields:
126                dirty_fields_values[field] = self._parsers_to_fields[field](value)
127            else:
128                dirty_fields_values[field] = value
129        return dirty_fields_values
130
131    @classmethod
132    def _initialize(cls, allspice_client: AllSpice, api_object: Self, result: Mapping):
133        super()._initialize(allspice_client, api_object, result)
134        for name in cls._patchable_fields:
135            cls._add_write_property(name, None, api_object)
136
137    @classmethod
138    def _add_write_property(cls, name: str, value: Any, api_object: Self):
139        if not hasattr(api_object, "_" + name):
140            setattr(api_object, "_" + name, value)
141        prop = property(
142            (lambda n: lambda self: self._get_var(n))(name),
143            (lambda n: lambda self, v: self.__set_var(n, v))(name),
144        )
145        setattr(cls, name, prop)
146
147    def __set_var(self, name: str, value: Any):
148        if self.deleted:
149            raise ObjectIsInvalid()
150        self._dirty_fields.add(name)
151        setattr(self, "_" + name, value)
ApiObject(allspice_client: allspice.AllSpice)
95    def __init__(self, allspice_client: AllSpice):
96        super().__init__(allspice_client)
97        self._dirty_fields = set()
def commit(self):
116    def commit(self):
117        raise NotImplementedError()
def get_dirty_fields(self) -> dict[str, typing.Any]:
121    def get_dirty_fields(self) -> dict[str, Any]:
122        dirty_fields_values = {}
123        for field in self._dirty_fields:
124            value = getattr(self, field)
125            if field in self._parsers_to_fields:
126                dirty_fields_values[field] = self._parsers_to_fields[field](value)
127            else:
128                dirty_fields_values[field] = value
129        return dirty_fields_values