1+ import posixpath
12import warnings
23from abc import ABC , abstractmethod
34from dataclasses import dataclass
@@ -51,84 +52,105 @@ def __init__(self, params: ResourceParameters) -> None:
5152
5253
5354class Active (ABC , Resource ):
54- def __init__ (self , ctx : Context , parent : Optional [ "Active" ] = None , ** kwargs ):
55- """A base class representing an active resource.
55+ def __init__ (self , ctx : Context , path : str , / , ** attributes ):
56+ """A dict abstraction for any HTTP endpoint that returns a singular resource.
5657
5758 Extends the `Resource` class and provides additional functionality for via the session context and an optional parent resource.
5859
5960 Parameters
6061 ----------
6162 ctx : Context
6263 The context object containing the session and URL for API interactions.
63- parent : Optional[Active], optional
64- An optional parent resource that establishes a hierarchical relationship, by default None.
65- **kwargs : dict
66- Additional keyword arguments passed to the parent `Resource` class.
64+ path : str
65+ The HTTP path component for the resource endpoint
66+ **attributes : dict
67+ Resource attributes passed
6768 """
6869 params = ResourceParameters (ctx .session , ctx .url )
69- super ().__init__ (params , ** kwargs )
70+ super ().__init__ (params , ** attributes )
7071 self ._ctx = ctx
71- self ._parent = parent
72+ self ._path = path
7273
7374
7475T = TypeVar ("T" , bound = "Active" )
7576"""A type variable that is bound to the `Active` class"""
7677
7778
7879class ActiveSequence (ABC , Generic [T ], Sequence [T ]):
79- def __init__ (self , ctx : Context , parent : Optional [Active ] = None ):
80- """A sequence abstraction for any HTTP GET endpoint that returns a collection.
80+ """A sequence for any HTTP GET endpoint that returns a collection."""
81+
82+ _cache : Optional [List [T ]]
8183
82- It lazily fetches data on demand, caches the results, and allows for standard sequence operations like indexing and slicing.
84+ def __init__ (self , ctx : Context , path : str , uid : str = "guid" ):
85+ """A sequence abstraction for any HTTP GET endpoint that returns a collection.
8386
8487 Parameters
8588 ----------
8689 ctx : Context
87- The context object that holds the HTTP session used for sending the GET request.
88- parent : Optional[Active], optional
89- An optional parent resource to establish a nested relationship, by default None.
90+ The context object containing the session and URL for API interactions.
91+ path : str
92+ The HTTP path component for the collection endpoint
93+ uid : str, optional
94+ The field name of that uniquely identifiers an instance of T, by default "guid"
9095 """
9196 super ().__init__ ()
9297 self ._ctx = ctx
93- self ._parent = parent
94- self ._cache : Optional [List [T ]] = None
98+ self ._path = path
99+ self ._uid = uid
100+ self ._cache = None
95101
96- @property
97102 @abstractmethod
98- def _endpoint (self ) -> str :
103+ def _create_instance (self , path : str , / , ** kwargs : Any ) -> T :
104+ """Create an instance of 'T'."""
105+ raise NotImplementedError ()
106+
107+ def reload (self ) -> Self :
108+ """Reloads the collection from Connect.
109+
110+ Returns
111+ -------
112+ Self
99113 """
100- Abstract property to define the endpoint URL for the GET request.
114+ self ._cache = None
115+ return self
116+
117+ def _fetch (self ) -> List [T ]:
118+ """Fetch the collection.
101119
102- Subclasses must implement this property to return the API endpoint URL that will
103- be queried to fetch the data.
120+ Fetches the collection directly from Connect. This operation does not effect the cache state.
104121
105122 Returns
106123 -------
107- str
108- The API endpoint URL.
124+ List[T]
109125 """
110- raise NotImplementedError ()
126+ endpoint = self ._ctx .url + self ._path
127+ response = self ._ctx .session .get (endpoint )
128+ results = response .json ()
129+ return [self ._to_instance (result ) for result in results ]
130+
131+ def _to_instance (self , result : dict ) -> T :
132+ """Converts a result into an instance of T."""
133+ uid = result [self ._uid ]
134+ path = posixpath .join (self ._path , uid )
135+ return self ._create_instance (path , ** result )
111136
112137 @property
113138 def _data (self ) -> List [T ]:
114- """
115- Fetch and cache the data from the API.
139+ """Get the collection.
116140
117- This method sends a GET request to the `_endpoint` and parses the response as a list of JSON objects.
118- Each JSON object is used to instantiate an item of type `T` using the class specified by `_cls`.
119- The results are cached after the first request and reused for subsequent access unless reloaded.
141+ Fetches the collection from Connect and caches the result. Subsequent invocations return the cached results unless the cache is explicitly reset.
120142
121143 Returns
122144 -------
123145 List[T]
124- A list of items of type `T` representing the fetched data.
125- """
126- if self ._cache :
127- return self ._cache
128146
129- response = self ._ctx .session .get (self ._endpoint )
130- results = response .json ()
131- self ._cache = [self ._create_instance (** result ) for result in results ]
147+ See Also
148+ --------
149+ cached
150+ reload
151+ """
152+ if self ._cache is None :
153+ self ._cache = self ._fetch ()
132154 return self ._cache
133155
134156 @overload
@@ -149,52 +171,18 @@ def __str__(self) -> str:
149171 def __repr__ (self ) -> str :
150172 return repr (self ._data )
151173
152- @abstractmethod
153- def _create_instance (self , ** kwargs ) -> T :
154- """Create an instance of 'T'.
155174
156- Returns
157- -------
158- T
159- """
160- raise NotImplementedError ()
175+ class ActiveFinderMethods (ActiveSequence [T ], ABC ):
176+ """Finder methods.
161177
162- def reload (self ) -> Self :
163- """
164- Clear the cache and reload the data from the API on the next access.
165-
166- Returns
167- -------
168- ActiveSequence
169- The current instance with cleared cache, ready to reload data on next access.
170- """
171- self ._cache = None
172- return self
173-
174-
175- class ActiveFinderMethods (ActiveSequence [T ], ABC , Generic [T ]):
176- def __init__ (self , ctx : Context , parent : Optional [Active ] = None , uid : str = "guid" ):
177- """Finder methods.
178-
179- Provides various finder methods for locating records in any endpoint supporting HTTP GET requests.
180-
181- Parameters
182- ----------
183- ctx : Context
184- The context containing the HTTP session used to interact with the API.
185- parent : Optional[Active], optional
186- Optional parent resource for maintaining hierarchical relationships, by default None
187- uid : str, optional
188- The default field name used to uniquely identify records, by default "guid"
189- """
190- super ().__init__ (ctx , parent )
191- self ._uid = uid
178+ Provides various finder methods for locating records in any endpoint supporting HTTP GET requests.
179+ """
192180
193181 def find (self , uid ) -> T :
194182 """
195183 Find a record by its unique identifier.
196184
197- Fetches a record either by searching the cache or by making a GET request to the endpoint .
185+ Fetches the record from Connect by it's identifier .
198186
199187 Parameters
200188 ----------
@@ -204,26 +192,11 @@ def find(self, uid) -> T:
204192 Returns
205193 -------
206194 T
207-
208- Raises
209- ------
210- ValueError
211- If no record is found.
212195 """
213- # todo - add some more comments about this
214- if self ._cache :
215- conditions = {self ._uid : uid }
216- result = self .find_by (** conditions )
217- else :
218- endpoint = self ._endpoint + uid
219- response = self ._ctx .session .get (endpoint )
220- result = response .json ()
221- result = self ._create_instance (** result )
222-
223- if not result :
224- raise ValueError (f"Failed to find instance where { self ._uid } is '{ uid } '" )
225-
226- return result
196+ endpoint = self ._ctx .url + self ._path + uid
197+ response = self ._ctx .session .get (endpoint )
198+ result = response .json ()
199+ return self ._to_instance (result )
227200
228201 def find_by (self , ** conditions : Any ) -> Optional [T ]:
229202 """
0 commit comments