Skip to content

Commit a601c6f

Browse files
schloerketdstein
andcommitted
Leverage context's client to make requests
Co-Authored-By: Taylor Steinberg <[email protected]>
1 parent c6be01f commit a601c6f

File tree

7 files changed

+39
-208
lines changed

7 files changed

+39
-208
lines changed

src/posit/connect/_api.py

Lines changed: 2 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
# TODO-barret-future; Piecemeal migrate everything to leverage `ApiDictEndpoint` and `ApiListEndpoint` classes.
1+
# TODO-barret-future; Piecemeal migrate everything to leverage `ApiDictEndpoint`
22
# TODO-barret-future; Merge any trailing behavior of `Active` or `ActiveList` into the new classes.
33

44
from __future__ import annotations
55

6-
import itertools
7-
import posixpath
8-
from abc import ABC, abstractmethod
96
from collections.abc import Mapping
10-
from typing import TYPE_CHECKING, Any, Generator, Generic, Optional, TypeVar, cast, overload
7+
from typing import TYPE_CHECKING, Any, Optional, cast
118

129
from ._api_call import ApiCallMixin, get_api
1310
from ._json import Jsonifiable, JsonifiableDict, ResponseAttrs
@@ -143,136 +140,3 @@ def __init__(
143140
super().__init__(attrs)
144141
self._ctx = ctx
145142
self._path = path
146-
147-
148-
T = TypeVar("T", bound="ReadOnlyDict")
149-
"""A type variable that is bound to the `Active` class"""
150-
151-
152-
class ApiListEndpoint(ApiCallMixin, Generic[T], ABC, object):
153-
"""A HTTP GET endpoint that can fetch a collection."""
154-
155-
def __init__(self, *, ctx: Context, path: str, uid_key: str = "guid") -> None:
156-
"""A sequence abstraction for any HTTP GET endpoint that returns a collection.
157-
158-
Parameters
159-
----------
160-
ctx : Context
161-
The context object containing the session and URL for API interactions.
162-
path : str
163-
The HTTP path component for the collection endpoint
164-
uid_key : str, optional
165-
The field name of that uniquely identifiers an instance of T, by default "guid"
166-
"""
167-
super().__init__()
168-
self._ctx = ctx
169-
self._path = path
170-
self._uid_key = uid_key
171-
172-
@abstractmethod
173-
def _create_instance(self, path: str, /, **kwargs: Any) -> T:
174-
"""Create an instance of 'T'."""
175-
raise NotImplementedError()
176-
177-
def fetch(self) -> Generator[T, None, None]:
178-
"""Fetch the collection.
179-
180-
Fetches the collection directly from Connect. This operation does not effect the cache state.
181-
182-
Returns
183-
-------
184-
List[T]
185-
"""
186-
results: Jsonifiable = self._get_api()
187-
results_list = cast(list[JsonifiableDict], results)
188-
for result in results_list:
189-
yield self._to_instance(result)
190-
191-
def __iter__(self) -> Generator[T, None, None]:
192-
return self.fetch()
193-
194-
def _to_instance(self, result: dict) -> T:
195-
"""Converts a result into an instance of T."""
196-
uid = result[self._uid_key]
197-
path = posixpath.join(self._path, uid)
198-
return self._create_instance(path, **result)
199-
200-
@overload
201-
def __getitem__(self, index: int) -> T: ...
202-
203-
@overload
204-
def __getitem__(self, index: slice) -> Generator[T, None, None]: ...
205-
206-
def __getitem__(self, index: int | slice) -> T | Generator[T, None, None]:
207-
if isinstance(index, slice):
208-
results = itertools.islice(self.fetch(), index.start, index.stop, index.step)
209-
for result in results:
210-
yield result
211-
else:
212-
return list(itertools.islice(self.fetch(), index, index + 1))[0]
213-
214-
# def __len__(self) -> int:
215-
# return len(self.fetch())
216-
217-
def __str__(self) -> str:
218-
return self.__repr__()
219-
220-
def __repr__(self) -> str:
221-
# Jobs - 123 items
222-
return repr(
223-
f"{self.__class__.__name__} - { len(list(self.fetch())) } items - {self._path}"
224-
)
225-
226-
def find(self, uid: str) -> T | None:
227-
"""
228-
Find a record by its unique identifier.
229-
230-
Fetches the record from Connect by it's identifier.
231-
232-
Parameters
233-
----------
234-
uid : str
235-
The unique identifier of the record.
236-
237-
Returns
238-
-------
239-
:
240-
Single instance of T if found, else None
241-
"""
242-
result: Jsonifiable = self._get_api(uid)
243-
result_obj = cast(JsonifiableDict, result)
244-
245-
return self._to_instance(result_obj)
246-
247-
def find_by(self, **conditions: Any) -> T | None:
248-
"""
249-
Find the first record matching the specified conditions.
250-
251-
There is no implied ordering, so if order matters, you should specify it yourself.
252-
253-
Parameters
254-
----------
255-
**conditions : Any
256-
257-
Returns
258-
-------
259-
T
260-
The first record matching the conditions, or `None` if no match is found.
261-
"""
262-
results = self.fetch()
263-
264-
conditions_items = conditions.items()
265-
266-
# Get the first item of the generator that matches the conditions
267-
# If no item is found, return None
268-
return next(
269-
(
270-
# Return result
271-
result
272-
# Iterate through `results` generator
273-
for result in results
274-
# If all `conditions`'s key/values are found in `result`'s key/values...
275-
if result.items() >= conditions_items
276-
),
277-
None,
278-
)

src/posit/connect/content.py

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,7 @@ def create_repository(
294294
def delete(self) -> None:
295295
"""Delete the content item."""
296296
path = f"v1/content/{self['guid']}"
297-
url = self._ctx.url + path
298-
self._ctx.session.delete(url)
297+
self._ctx.client.delete(path)
299298

300299
def deploy(self) -> tasks.Task:
301300
"""Deploy the content.
@@ -314,8 +313,7 @@ def deploy(self) -> tasks.Task:
314313
None
315314
"""
316315
path = f"v1/content/{self['guid']}/deploy"
317-
url = self._ctx.url + path
318-
response = self._ctx.session.post(url, json={"bundle_id": None})
316+
response = self._ctx.client.post(path, json={"bundle_id": None})
319317
result = response.json()
320318
ts = tasks.Tasks(self.params)
321319
return ts.get(result["task_id"])
@@ -441,8 +439,7 @@ def update(
441439
-------
442440
None
443441
"""
444-
url = self._ctx.url + f"v1/content/{self['guid']}"
445-
response = self._ctx.session.patch(url, json=attrs)
442+
response = self._ctx.client.patch(f"v1/content/{self['guid']}", json=attrs)
446443
super().update(**response.json())
447444

448445
# Relationships
@@ -607,9 +604,7 @@ def create(
607604
-------
608605
ContentItem
609606
"""
610-
path = "v1/content"
611-
url = self._ctx.url + path
612-
response = self._ctx.session.post(url, json=attrs)
607+
response = self._ctx.client.post("v1/content", json=attrs)
613608
return ContentItem(self._ctx, **response.json())
614609

615610
@overload
@@ -695,9 +690,7 @@ def find(self, include: Optional[str | list[Any]] = None, **conditions) -> List[
695690
if self.owner_guid:
696691
conditions["owner_guid"] = self.owner_guid
697692

698-
path = "v1/content"
699-
url = self._ctx.url + path
700-
response = self._ctx.session.get(url, params=conditions)
693+
response = self._ctx.client.get("v1/content", params=conditions)
701694
return [
702695
ContentItem(
703696
self._ctx,
@@ -868,7 +861,5 @@ def get(self, guid: str) -> ContentItem:
868861
-------
869862
ContentItem
870863
"""
871-
path = f"v1/content/{guid}"
872-
url = self._ctx.url + path
873-
response = self._ctx.session.get(url)
864+
response = self._ctx.client.get(f"v1/content/{guid}")
874865
return ContentItem(self._ctx, **response.json())

src/posit/connect/groups.py

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ def delete(self) -> None:
6262
group.delete()
6363
```
6464
"""
65-
path = f"v1/groups/{self['guid']}"
66-
url = self._ctx.url + path
67-
self._ctx.session.delete(url)
65+
self._ctx.client.delete(f"v1/groups/{self['guid']}")
6866

6967

7068
class GroupMembers(Resources):
@@ -129,9 +127,10 @@ def add(self, user: Optional[User] = None, /, *, user_guid: Optional[str] = None
129127
if not user_guid:
130128
raise ValueError("`user_guid=` should not be empty.")
131129

132-
path = f"v1/groups/{self._group_guid}/members"
133-
url = self._ctx.url + path
134-
self._ctx.session.post(url, json={"user_guid": user_guid})
130+
self._ctx.client.post(
131+
f"v1/groups/{self._group_guid}/members",
132+
json={"user_guid": user_guid},
133+
)
135134

136135
@overload
137136
def delete(self, user: User, /) -> None: ...
@@ -189,9 +188,7 @@ def delete(self, user: Optional[User] = None, /, *, user_guid: Optional[str] = N
189188
if not user_guid:
190189
raise ValueError("`user_guid=` should not be empty.")
191190

192-
path = f"v1/groups/{self._group_guid}/members/{user_guid}"
193-
url = self._ctx.url + path
194-
self._ctx.session.delete(url)
191+
self._ctx.client.delete(f"v1/groups/{self._group_guid}/members/{user_guid}")
195192

196193
def find(self) -> list[User]:
197194
"""Find group members.
@@ -254,9 +251,10 @@ def count(self) -> int:
254251
--------
255252
* https://docs.posit.co/connect/api/#get-/v1/groups/-group_guid-/members
256253
"""
257-
path = f"v1/groups/{self._group_guid}/members"
258-
url = self._ctx.url + path
259-
response = self._ctx.session.get(url, params={"page_size": 1})
254+
response = self._ctx.client.get(
255+
f"v1/groups/{self._group_guid}/members",
256+
params={"page_size": 1},
257+
)
260258
result = response.json()
261259
return result["total"]
262260

@@ -307,9 +305,7 @@ def create(self, **kwargs) -> Group:
307305
-------
308306
Group
309307
"""
310-
path = "v1/groups"
311-
url = self._ctx.url + path
312-
response = self._ctx.session.post(url, json=kwargs)
308+
response = self._ctx.client.post("v1/groups", json=kwargs)
313309
return Group(self._ctx, **response.json())
314310

315311
@overload
@@ -405,8 +401,7 @@ def get(self, guid: str) -> Group:
405401
--------
406402
* https://docs.posit.co/connect/api/#get-/v1/groups
407403
"""
408-
url = self._ctx.url + f"v1/groups/{guid}"
409-
response = self._ctx.session.get(url)
404+
response = self._ctx.client.get(f"v1/groups/{guid}")
410405
return Group(
411406
self._ctx,
412407
**response.json(),

src/posit/connect/jobs.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,7 @@ def destroy(self) -> None:
117117
----
118118
This action requires administrator, owner, or collaborator privileges.
119119
"""
120-
endpoint = self._ctx.url + self._path
121-
self._ctx.session.delete(endpoint)
120+
self._ctx.client.delete(self._path)
122121

123122

124123
class Jobs(ActiveFinderMethods[Job], ActiveSequence[Job]):

src/posit/connect/resources.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,7 @@ def fetch(self, **conditions: Any) -> Iterable[T]:
129129
-------
130130
List[T]
131131
"""
132-
endpoint = self._ctx.url + self._path
133-
response = self._ctx.session.get(endpoint, params=conditions)
132+
response = self._ctx.client.get(self._path, params=conditions)
134133
results = response.json()
135134
return [self._to_instance(result) for result in results]
136135

@@ -212,8 +211,7 @@ def find(self, uid) -> T:
212211
-------
213212
T
214213
"""
215-
endpoint = self._ctx.url + self._path + uid
216-
response = self._ctx.session.get(endpoint)
214+
response = self._ctx.client.get(f"{self._path}/{uid}")
217215
result = response.json()
218216
return self._to_instance(result)
219217

0 commit comments

Comments
 (0)