Skip to content

Commit 1cce667

Browse files
committed
Add/test many tag, tags, and content item tags methods
1 parent 4051bfb commit 1cce667

File tree

13 files changed

+1589
-0
lines changed

13 files changed

+1589
-0
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
from typing import TYPE_CHECKING
2+
3+
import pytest
4+
from packaging import version
5+
6+
from posit import connect
7+
8+
from . import CONNECT_VERSION
9+
10+
if TYPE_CHECKING:
11+
from posit.connect.content import ContentItem
12+
13+
14+
# add integration tests here!
15+
@pytest.mark.skipif(
16+
CONNECT_VERSION < version.parse("2024.10.0-dev"),
17+
reason="Packages API unavailable",
18+
)
19+
class TestTags:
20+
@classmethod
21+
def setup_class(cls):
22+
cls.client = connect.Client()
23+
cls.contentA = cls.client.content.create(name=cls.__name__)
24+
cls.contentB = cls.client.content.create(name=cls.__name__)
25+
cls.contentC = cls.client.content.create(
26+
name=cls.__name__,
27+
)
28+
29+
@classmethod
30+
def teardown_class(cls):
31+
assert len(cls.client.tags.find()) == 0
32+
33+
def test_tags_find(self):
34+
tags = self.client.tags.find()
35+
assert len(tags) == 0
36+
37+
def test_tags_create_destroy(self):
38+
tagA = self.client.tags.create(name="tagA")
39+
tagB = self.client.tags.create(name="tagB")
40+
tagC = self.client.tags.create(name="tagC", parent=self.tagA)
41+
42+
assert len(self.client.tags.find()) == 3
43+
44+
tagA.destroy()
45+
tagB.destroy()
46+
## Deleted when tag A is deleted
47+
# tagC.destroy()
48+
49+
assert len(self.client.tags.find()) == 0
50+
51+
def test_tag_descendants(self):
52+
# Have created tags persist
53+
self.tagA = self.client.tags.create(name="tagA")
54+
self.tagB = self.client.tags.create(name="tagB")
55+
self.tagC = self.client.tags.create(name="tagC", parent=self.tagA)
56+
self.tagD = self.client.tags.create(name="tagD", parent=self.tagC)
57+
58+
assert self.tagA.descendant_tags.find() == [self.tagC, self.tagD]
59+
60+
assert len(self.tagB.descendant_tags.find()) == 0
61+
assert len(self.tagC.descendant_tags.find()) == [self.tagD]
62+
63+
def test_tag_children(self):
64+
assert self.tagA.children_tags.find() == [self.tagC]
65+
assert self.tagB.children_tags.find() == []
66+
assert self.tagC.children_tags.find() == [self.tagD]
67+
68+
def test_tag_parent(self):
69+
assert self.tagA.parent_tag is None
70+
assert self.tagB.parent_tag is None
71+
assert self.tagC.parent_tag == self.tagA
72+
assert self.tagD.parent_tag == self.tagC
73+
74+
def test_content_a_tags(self):
75+
assert len(self.contentA.tags.find()) == 0
76+
77+
self.contentA.tags.add(self.tagA)
78+
self.contentA.tags.add(self.tagB)
79+
80+
# tagB, tagC, tagA (parent of tagC)
81+
assert len(self.contentA.tags.find()) == 3
82+
83+
self.contentA.tags.delete(self.tagB)
84+
assert len(self.contentA.tags.find()) == 2
85+
86+
# Removes tagC and tagA (parent of tagC)
87+
self.contentA.tags.delete(self.tagA)
88+
assert len(self.contentA.tags.find()) == 0
89+
90+
def test_tags_content_items(self):
91+
assert len(self.tagA.content_items.find()) == 0
92+
assert len(self.tagB.content_items.find()) == 0
93+
assert len(self.tagC.content_items.find()) == 0
94+
95+
self.contentA.tags.add(self.tagA)
96+
self.contentA.tags.add(self.tagB)
97+
98+
self.contentB.tags.add(self.tagA)
99+
self.contentB.tags.add(self.tagC)
100+
101+
self.contentC.tags.add(self.tagC)
102+
103+
assert len(self.contentA.tags.find()) == 2
104+
assert len(self.contentB.tags.find()) == 2
105+
assert len(self.contentC.tags.find()) == 1
106+
107+
assert len(self.tagA.content_items.find()) == 2
108+
assert len(self.tagB.content_items.find()) == 1
109+
assert len(self.tagC.content_items.find()) == 2
110+
111+
# Make sure unique content items are found
112+
content_items_list: list[ContentItem] = []
113+
for tag in [self.tagA, *self.tagA.descendant_tags.find()]:
114+
content_items_list.extend(tag.content_items.find())
115+
# Get unique items
116+
content_items_list = list(set(content_items_list))
117+
118+
assert content_items_list == [self.contentA, self.contentB, self.contentC]
119+
120+
self.contentA.tags.delete(self.tagA, self.tagB)
121+
self.contentB.tags.delete(self.tagA)
122+
self.contentC.tags.delete(self.tagC)
123+
124+
assert len(self.tagA.content_items.find()) == 0
125+
assert len(self.tagB.content_items.find()) == 0
126+
assert len(self.tagC.content_items.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.resource_params)
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
@@ -28,6 +28,7 @@
2828
from .packages import ContentPackagesMixin as PackagesMixin
2929
from .permissions import Permissions
3030
from .resources import Resource, ResourceParameters, Resources
31+
from .tags import ContentItemTags
3132
from .vanities import VanityMixin
3233
from .variants import Variants
3334

@@ -496,6 +497,16 @@ def is_rendered(self) -> bool:
496497
"quarto-static",
497498
}
498499

500+
@property
501+
def tags(self) -> ContentItemTags:
502+
path = f"v1/content/{self['guid']}/tags"
503+
return ContentItemTags(
504+
self._ctx,
505+
path,
506+
tags_path="v1/tags",
507+
content_guid=self["guid"],
508+
)
509+
499510

500511
class Content(Resources):
501512
"""Content resource.

0 commit comments

Comments
 (0)