Skip to content

Commit cbf3634

Browse files
authored
feat: Add/test many tag, tags, and content item tags methods (#346)
1 parent b884e65 commit cbf3634

File tree

16 files changed

+1707
-1
lines changed

16 files changed

+1707
-1
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
from posit import connect
2+
3+
4+
class TestTags:
5+
@classmethod
6+
def setup_class(cls):
7+
cls.client = connect.Client()
8+
cls.contentA = cls.client.content.create(name="Content_A")
9+
cls.contentB = cls.client.content.create(name="Content_B")
10+
cls.contentC = cls.client.content.create(name="Content_C")
11+
12+
@classmethod
13+
def teardown_class(cls):
14+
assert len(cls.client.tags.find()) == 0
15+
cls.contentA.delete()
16+
cls.contentB.delete()
17+
cls.contentC.delete()
18+
assert len(cls.client.content.find()) == 0
19+
20+
def test_tags_find(self):
21+
tags = self.client.tags.find()
22+
assert len(tags) == 0
23+
24+
def test_tags_create_destroy(self):
25+
tagA = self.client.tags.create(name="tagA")
26+
tagB = self.client.tags.create(name="tagB")
27+
tagC = self.client.tags.create(name="tagC", parent=tagA)
28+
29+
assert len(self.client.tags.find()) == 3
30+
31+
tagA.destroy()
32+
tagB.destroy()
33+
## Deleted when tag A is deleted
34+
# tagC.destroy()
35+
36+
assert len(self.client.tags.find()) == 0
37+
38+
def test_tag_descendants(self):
39+
tagA = self.client.tags.create(name="tagA")
40+
tagB = self.client.tags.create(name="tagB")
41+
tagC = self.client.tags.create(name="tagC", parent=tagA)
42+
tagD = self.client.tags.create(name="tagD", parent=tagC)
43+
44+
assert tagA.descendant_tags.find() == [tagC, tagD]
45+
46+
assert len(tagB.descendant_tags.find()) == 0
47+
assert tagC.descendant_tags.find() == [tagD]
48+
49+
# cleanup
50+
tagA.destroy()
51+
tagB.destroy()
52+
assert len(self.client.tags.find()) == 0
53+
54+
def test_tag_children(self):
55+
tagA = self.client.tags.create(name="tagA_children")
56+
tagB = self.client.tags.create(name="tagB_children")
57+
tagC = self.client.tags.create(name="tagC_children", parent=tagA)
58+
tagD = self.client.tags.create(name="tagD_children", parent=tagC)
59+
60+
assert tagA.child_tags.find() == [tagC]
61+
assert tagB.child_tags.find() == []
62+
assert tagC.child_tags.find() == [tagD]
63+
64+
# cleanup
65+
tagA.destroy()
66+
tagB.destroy()
67+
assert len(self.client.tags.find()) == 0
68+
69+
def test_tag_parent(self):
70+
tagA = self.client.tags.create(name="tagA_parent")
71+
tagB = self.client.tags.create(name="tagB_parent")
72+
tagC = self.client.tags.create(name="tagC_parent", parent=tagA)
73+
tagD = self.client.tags.create(name="tagD_parent", parent=tagC)
74+
75+
assert tagA.parent_tag is None
76+
assert tagB.parent_tag is None
77+
assert tagC.parent_tag == tagA
78+
assert tagD.parent_tag == tagC
79+
80+
# cleanup
81+
tagA.destroy()
82+
tagB.destroy()
83+
assert len(self.client.tags.find()) == 0
84+
85+
def test_content_item_tags(self):
86+
tagRoot = self.client.tags.create(name="tagRoot_content_item_tags")
87+
tagA = self.client.tags.create(name="tagA_content_item_tags", parent=tagRoot)
88+
tagB = self.client.tags.create(name="tagB_content_item_tags", parent=tagRoot)
89+
tagC = self.client.tags.create(name="tagC_content_item_tags", parent=tagA)
90+
tagD = self.client.tags.create(name="tagD_content_item_tags", parent=tagC)
91+
92+
assert len(self.contentA.tags.find()) == 0
93+
94+
self.contentA.tags.add(tagD)
95+
self.contentA.tags.add(tagB)
96+
97+
# tagB, tagD, tagC (parent of tagD), tagA (parent of tagC)
98+
# tagD + tagC
99+
# tagRoot is considered a "category" and is "not a tag"
100+
assert len(self.contentA.tags.find()) == 4
101+
102+
# Removes tagB
103+
self.contentA.tags.delete(tagB)
104+
assert len(self.contentA.tags.find()) == 4 - 1
105+
106+
# Removes tagC and tagD (parent of tagC)
107+
self.contentA.tags.delete(tagC)
108+
assert len(self.contentA.tags.find()) == 4 - 1 - 2
109+
110+
# cleanup
111+
tagRoot.destroy()
112+
assert len(self.client.tags.find()) == 0
113+
114+
def test_tag_content_items(self):
115+
tagRoot = self.client.tags.create(name="tagRoot_tag_content_items")
116+
tagA = self.client.tags.create(name="tagA_tag_content_items", parent=tagRoot)
117+
tagB = self.client.tags.create(name="tagB_tag_content_items", parent=tagRoot)
118+
tagC = self.client.tags.create(name="tagC_tag_content_items", parent=tagA)
119+
tagD = self.client.tags.create(name="tagD_tag_content_items", parent=tagC)
120+
121+
assert len(tagA.content_items.find()) == 0
122+
assert len(tagB.content_items.find()) == 0
123+
assert len(tagC.content_items.find()) == 0
124+
assert len(tagD.content_items.find()) == 0
125+
126+
self.contentA.tags.add(tagD)
127+
self.contentA.tags.add(tagB)
128+
129+
self.contentB.tags.add(tagA)
130+
self.contentB.tags.add(tagC)
131+
132+
self.contentC.tags.add(tagC)
133+
134+
assert len(self.contentA.tags.find()) == 4
135+
assert len(self.contentB.tags.find()) == 2
136+
assert len(self.contentC.tags.find()) == 2
137+
138+
assert len(tagA.content_items.find()) == 3
139+
assert len(tagB.content_items.find()) == 1
140+
assert len(tagC.content_items.find()) == 3
141+
142+
# Make sure unique content items are found
143+
content_items = tagA.content_items.find()
144+
assert len(content_items) == 3
145+
146+
content_item_guids = {content_item["guid"] for content_item in content_items}
147+
assert content_item_guids == {
148+
self.contentA["guid"],
149+
self.contentB["guid"],
150+
self.contentC["guid"],
151+
}
152+
153+
self.contentA.tags.delete(tagRoot)
154+
self.contentB.tags.delete(tagRoot)
155+
self.contentC.tags.delete(tagRoot)
156+
157+
assert len(tagA.content_items.find()) == 0
158+
assert len(tagB.content_items.find()) == 0
159+
assert len(tagC.content_items.find()) == 0
160+
assert len(tagD.content_items.find()) == 0
161+
162+
# cleanup
163+
tagRoot.destroy()
164+
assert len(self.client.tags.find()) == 0

src/posit/connect/client.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
from requests import Response, Session
88

9+
from posit.connect.tags import Tags
10+
911
from . import hooks, me
1012
from .auth import Auth
1113
from .config import Config
@@ -229,6 +231,28 @@ def content(self) -> Content:
229231
"""
230232
return Content(self._ctx)
231233

234+
@property
235+
def tags(self) -> Tags:
236+
"""
237+
The tags resource interface.
238+
239+
Returns
240+
-------
241+
Tags
242+
The tags resource instance.
243+
244+
Examples
245+
--------
246+
```python
247+
import posit
248+
249+
client = posit.connect.Client()
250+
251+
tags = client.tags.find()
252+
```
253+
"""
254+
return Tags(self._ctx, "v1/tags")
255+
232256
@property
233257
def metrics(self) -> Metrics:
234258
"""

src/posit/connect/content.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from .packages import ContentPackagesMixin as PackagesMixin
2828
from .permissions import Permissions
2929
from .resources import Resource, ResourceParameters, Resources
30+
from .tags import ContentItemTags
3031
from .vanities import VanityMixin
3132
from .variants import Variants
3233

@@ -500,6 +501,16 @@ def is_rendered(self) -> bool:
500501
"quarto-static",
501502
}
502503

504+
@property
505+
def tags(self) -> ContentItemTags:
506+
path = f"v1/content/{self['guid']}/tags"
507+
return ContentItemTags(
508+
self._ctx,
509+
path,
510+
tags_path="v1/tags",
511+
content_guid=self["guid"],
512+
)
513+
503514

504515
class Content(Resources):
505516
"""Content resource.

src/posit/connect/permissions.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,6 @@ def destroy(self, permission: str | Group | User | Permission, /) -> None:
272272
permission_obj = self.find_one(
273273
principal_guid=principal_guid,
274274
)
275-
print("Barret!", permission, principal_guid, permission_obj)
276275
if permission_obj is None:
277276
raise ValueError(f"Permission with principal_guid '{principal_guid}' not found.")
278277
elif isinstance(permission, Permission):

0 commit comments

Comments
 (0)