Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add User.groups and Group.members #341

Merged
merged 17 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions integration/tests/posit/connect/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ def test_get(self):
assert self.client.users.get(self.bill["guid"]) == self.bill
assert self.client.users.get(self.cole["guid"]) == self.cole

# Also tests Groups.members
def test_groups(self):
try:
unit_friends = self.client.groups.create(name="UnitFriends")
self.client.post(
f"/v1/groups/{unit_friends['guid']}/members",
json={"user_guid": self.bill["guid"]},
)
bill_groups = self.bill.groups.find()
assert len(bill_groups) == 1
assert bill_groups[0]["guid"] == unit_friends["guid"]
finally:
groups = self.client.groups.find(prefix="UnitFriends")
if len(groups) > 0:
unit_friends = groups[0]
unit_friends.delete()


class TestUserContent:
"""Checks behavior of the content attribute."""
Expand Down
10 changes: 5 additions & 5 deletions src/posit/connect/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def __init__(self, *args, **kwargs) -> None:
session.hooks["response"].append(hooks.handle_errors)
self.session = session
self.resource_params = ResourceParameters(session, self.cfg.url)
self._ctx = Context(self.session, self.cfg.url)
self._ctx = Context(self)

@property
def version(self) -> str | None:
Expand All @@ -180,7 +180,7 @@ def me(self) -> User:
User
The currently authenticated user.
"""
return me.get(self.resource_params)
return me.get(self._ctx)

@property
def groups(self) -> Groups:
Expand All @@ -191,7 +191,7 @@ def groups(self) -> Groups:
Groups
The groups resource interface.
"""
return Groups(self.resource_params)
return Groups(self._ctx)

@property
def tasks(self) -> Tasks:
Expand All @@ -215,7 +215,7 @@ def users(self) -> Users:
Users
The users resource instance.
"""
return Users(self.resource_params)
return Users(self._ctx)

@property
def content(self) -> Content:
Expand All @@ -227,7 +227,7 @@ def content(self) -> Content:
Content
The content resource instance.
"""
return Content(self.resource_params)
return Content(self._ctx)

@property
def metrics(self) -> Metrics:
Expand Down
55 changes: 30 additions & 25 deletions src/posit/connect/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from . import tasks
from ._api import ApiDictEndpoint, JsonifiableDict
from .bundles import Bundles
from .context import Context
from .env import EnvVars
from .errors import ClientError
from .jobs import JobsMixin
Expand All @@ -32,6 +31,7 @@
from .variants import Variants

if TYPE_CHECKING:
from .context import Context
from .tasks import Task


Expand Down Expand Up @@ -221,30 +221,32 @@ class _AttrsCreate(_AttrsBase):
@overload
def __init__(
self,
ctx: Context,
/,
params: ResourceParameters,
*,
guid: str,
) -> None: ...

@overload
def __init__(
self,
ctx: Context,
/,
params: ResourceParameters,
*,
guid: str,
**kwargs: Unpack[ContentItem._Attrs],
) -> None: ...

def __init__(
self,
ctx: Context,
/,
params: ResourceParameters,
*,
guid: str,
**kwargs: Unpack[ContentItem._AttrsNotRequired],
) -> None:
_assert_guid(guid)

ctx = Context(params.session, params.url)
path = f"v1/content/{guid}"
super().__init__(ctx, path, guid=guid, **kwargs)

Expand Down Expand Up @@ -291,8 +293,8 @@ def create_repository(
def delete(self) -> None:
"""Delete the content item."""
path = f"v1/content/{self['guid']}"
url = self.params.url + path
self.params.session.delete(url)
url = self._ctx.url + path
self._ctx.session.delete(url)

def deploy(self) -> tasks.Task:
"""Deploy the content.
Expand All @@ -311,8 +313,8 @@ def deploy(self) -> tasks.Task:
None
"""
path = f"v1/content/{self['guid']}/deploy"
url = self.params.url + path
response = self.params.session.post(url, json={"bundle_id": None})
url = self._ctx.url + path
response = self._ctx.session.post(url, json={"bundle_id": None})
result = response.json()
ts = tasks.Tasks(self.params)
return ts.get(result["task_id"])
Expand Down Expand Up @@ -367,8 +369,8 @@ def restart(self) -> None:
self.environment_variables.create(key, unix_epoch_in_seconds)
self.environment_variables.delete(key)
# GET via the base Connect URL to force create a new worker thread.
url = posixpath.join(dirname(self.params.url), f"content/{self['guid']}")
self.params.session.get(url)
url = posixpath.join(dirname(self._ctx.url), f"content/{self['guid']}")
self._ctx.session.get(url)
return None
else:
raise ValueError(
Expand Down Expand Up @@ -438,8 +440,8 @@ def update(
-------
None
"""
url = self.params.url + f"v1/content/{self['guid']}"
response = self.params.session.patch(url, json=attrs)
url = self._ctx.url + f"v1/content/{self['guid']}"
response = self._ctx.session.patch(url, json=attrs)
super().update(**response.json())

# Relationships
Expand All @@ -464,7 +466,9 @@ def owner(self) -> dict:
# If it's not included, we can retrieve the information by `owner_guid`
from .users import Users

self["owner"] = Users(self.params).get(self["owner_guid"])
self["owner"] = Users(
self._ctx,
).get(self["owner_guid"])
return self["owner"]

@property
Expand Down Expand Up @@ -512,12 +516,13 @@ class Content(Resources):

def __init__(
self,
params: ResourceParameters,
ctx: Context,
*,
owner_guid: str | None = None,
) -> None:
super().__init__(params)
super().__init__(ctx.client.resource_params)
self.owner_guid = owner_guid
self._ctx = ctx

def count(self) -> int:
"""Count the number of content items.
Expand Down Expand Up @@ -592,9 +597,9 @@ def create(
ContentItem
"""
path = "v1/content"
url = self.params.url + path
response = self.params.session.post(url, json=attrs)
return ContentItem(self.params, **response.json())
url = self._ctx.url + path
response = self._ctx.session.post(url, json=attrs)
return ContentItem(self._ctx, **response.json())

@overload
def find(
Expand Down Expand Up @@ -680,11 +685,11 @@ def find(self, include: Optional[str | list[Any]] = None, **conditions) -> List[
conditions["owner_guid"] = self.owner_guid

path = "v1/content"
url = self.params.url + path
response = self.params.session.get(url, params=conditions)
url = self._ctx.url + path
response = self._ctx.session.get(url, params=conditions)
return [
ContentItem(
self.params,
self._ctx,
**result,
)
for result in response.json()
Expand Down Expand Up @@ -853,6 +858,6 @@ def get(self, guid: str) -> ContentItem:
ContentItem
"""
path = f"v1/content/{guid}"
url = self.params.url + path
response = self.params.session.get(url)
return ContentItem(self.params, **response.json())
url = self._ctx.url + path
response = self._ctx.session.get(url)
return ContentItem(self._ctx, **response.json())
11 changes: 8 additions & 3 deletions src/posit/connect/context.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from __future__ import annotations

import functools
import weakref
from typing import TYPE_CHECKING, Protocol

from packaging.version import Version

if TYPE_CHECKING:
import requests

from .client import Client
from .urls import Url


Expand All @@ -28,9 +30,12 @@ def wrapper(instance: ContextManager, *args, **kwargs):


class Context:
def __init__(self, session: requests.Session, url: Url):
self.session = session
self.url = url
def __init__(self, client: Client):
self.session: requests.Session = client.session
self.url: Url = client.cfg.url
# Since this is a child object of the client, we use a weak reference to avoid circular
# references (which would prevent garbage collection)
self.client: Client = weakref.proxy(client)

@property
def version(self) -> str | None:
Expand Down
Loading
Loading