1313from amatino .internal .url_parameters import UrlParameters
1414from amatino .internal .constrained_string import ConstrainedString
1515from amatino .internal .encodable import Encodable
16- from amatino .amatino_error import AmatinoError
17- from typing import TypeVar
18- from typing import Optional
19- from typing import Type
20- from typing import Dict
21- from typing import Any
16+ from amatino .state import State
17+ from typing import TypeVar , Optional , Type , Dict , Any , List
2218from amatino .internal .immutable import Immutable
19+ from amatino .internal .session_decodable import SessionDecodable
20+ from amatino .internal .disposition import Disposition
21+ from amatino .internal .url_target import UrlTarget
2322
2423T = TypeVar ('T' , bound = 'Entity' )
2524
2625
27- class Entity :
26+ class Entity ( SessionDecodable ) :
2827 """
2928 An Amatino entity is a economic unit to be described by accounting
3029 information. Entities are described by accounts, transactions, and entries.
@@ -33,8 +32,11 @@ class Entity:
3332 of companies, a project, or even a person.
3433 """
3534 PATH = '/entities'
35+ LIST_PATH = '/entities/list'
3636 MAX_NAME_LENGTH = 1024
3737 MAX_DESCRIPTION_LENGTH = 4096
38+ MAX_NAME_SEARCH_LENGTH = 64
39+ MIN_NAME_SEARCH_LENGTH = 3
3840
3941 def __init__ (
4042 self ,
@@ -44,7 +46,8 @@ def __init__(
4446 description : str ,
4547 region_id : int ,
4648 owner_id : int ,
47- permissions_graph : PermissionsGraph
49+ permissions_graph : PermissionsGraph ,
50+ disposition : Disposition
4851 ) -> None :
4952
5053 self ._session = session
@@ -54,6 +57,7 @@ def __init__(
5457 self ._region_id = region_id
5558 self ._owner_id = owner_id
5659 self ._permissions_graph = permissions_graph
60+ self ._disposition = disposition
5761
5862 return
5963
@@ -64,6 +68,7 @@ def __init__(
6468 region_id = Immutable (lambda s : s ._region_id )
6569 owner_id = Immutable (lambda s : s ._owner_id )
6670 permissions_graph = Immutable (lambda s : s ._permissions_graph )
71+ disposition = Immutable (lambda s : s ._disposition )
6772
6873 @classmethod
6974 def create (
@@ -91,52 +96,38 @@ def create(
9196 False
9297 )
9398
94- created_entity = cls ._decode (request .response_data , session )
99+ created_entity = cls .decode (request .response_data , session )
95100
96101 return created_entity
97102
98103 @classmethod
99- def _decode (cls : Type [T ], data : list , session : Session ) -> T :
104+ def decode (cls : Type [T ], data : Any , session : Session ) -> T :
100105 """
101106 Return an Entity instance decoded from API response data
102107 """
103- assert isinstance (session , Session )
108+ if isinstance (data , list ):
109+ data = data [0 ]
104110
105- if not isinstance (data , list ):
106- raise AmatinoError ('Unexpected response format: ' + str (type (data )))
107-
108- if len (data ) < 1 :
109- raise AmatinoError ('Response list unexpectedly empty' )
110-
111- raw_entity = data [0 ]
112-
113- if not isinstance (raw_entity , dict ):
114- raise AmatinoError ('Unexpected response format' )
115- try :
116- entity = cls (
117- session = session ,
118- entity_id = raw_entity ['entity_id' ],
119- name = raw_entity ['name' ],
120- description = raw_entity ['description' ],
121- region_id = raw_entity ['region_id' ],
122- owner_id = raw_entity ['owner' ],
123- permissions_graph = PermissionsGraph (
124- raw_entity ['permissions_graph' ]
125- )
126- )
127- except KeyError as error :
128- raise AmatinoError (
129- 'Unexpected response format, missing key ' + error .args [0 ]
130- )
111+ assert isinstance (session , Session )
131112
132- return entity
113+ return cls (
114+ session = session ,
115+ entity_id = data ['entity_id' ],
116+ name = data ['name' ],
117+ description = data ['description' ],
118+ region_id = data ['region_id' ],
119+ owner_id = data ['owner' ],
120+ permissions_graph = PermissionsGraph (data ['permissions_graph' ]),
121+ disposition = Disposition .decode (data ['disposition' ])
122+ )
133123
134124 @classmethod
135125 def retrieve (
136126 cls : Type [T ],
137127 session : Session ,
138128 entity_id : str
139- ) -> T :
129+ ) -> Optional [T ]:
130+
140131 if not isinstance (session , Session ):
141132 raise TypeError ('session must be of type `Session`' )
142133
@@ -154,9 +145,70 @@ def retrieve(
154145 debug = False
155146 )
156147
157- entity = cls ._decode (request .response_data , session )
148+ return cls .optionally_decode (request .response_data , session )
149+
150+ @classmethod
151+ def retrieve_list (
152+ cls : Type [T ],
153+ session : Session ,
154+ state : State = State .ALL ,
155+ offset : int = 0 ,
156+ limit : int = 10 ,
157+ name_fragment : Optional [str ] = None
158+ ) -> List [T ]:
159+
160+ if not isinstance (session , Session ):
161+ raise TypeError ('session must be of type `amatino.Session`' )
162+
163+ if not isinstance (offset , int ):
164+ raise TypeError ('offset must be of type `int`' )
165+
166+ if not isinstance (limit , int ):
167+ raise TypeError ('limit must be of type `int`' )
158168
159- return entity
169+ if not isinstance (state , State ):
170+ raise TypeError ('state must be of type `amatino.State`' )
171+
172+ if name_fragment is not None :
173+ if not isinstance (name_fragment , str ):
174+ raise TypeError ('name_fragment must be of type `str`' )
175+ if len (name_fragment ) < cls .MIN_NAME_SEARCH_LENGTH :
176+ raise ValueError (
177+ 'name_fragment minimum length is {c} char' .format (
178+ c = str (cls .MIN_NAME_SEARCH_LENGTH )
179+ )
180+ )
181+ if len (name_fragment ) > cls .MAX_NAME_SEARCH_LENGTH :
182+ raise ValueError (
183+ 'name_fragment maximum length is {c} char' .format (
184+ c = str (cls .MAX_NAME_SEARCH_LENGTH )
185+ )
186+ )
187+
188+ url_targets = [
189+ UrlTarget ('limit' , limit ),
190+ UrlTarget ('offset' , offset ),
191+ UrlTarget ('state' , state .value )
192+ ]
193+
194+ if name_fragment is not None :
195+ url_targets .append (UrlTarget ('name' , name_fragment ))
196+
197+ url_parameters = UrlParameters (targets = url_targets )
198+
199+ request = ApiRequest (
200+ path = Entity .LIST_PATH ,
201+ method = HTTPMethod .GET ,
202+ credentials = session ,
203+ data = None ,
204+ url_parameters = url_parameters
205+ )
206+
207+ return cls .optionally_decode_many (
208+ data = request .response_data ,
209+ session = session ,
210+ default_to_empty_list = True
211+ )
160212
161213 def update (
162214 self ,
@@ -192,7 +244,7 @@ def update(
192244 url_parameters = None
193245 )
194246
195- updated_entity = Entity ._decode (request .response_data , self .session )
247+ updated_entity = Entity .decode (request .response_data , self .session )
196248
197249 return updated_entity
198250
0 commit comments