Skip to content

Commit 4edb9f1

Browse files
authored
feat: add packages and content packages (#313)
Adds packages support.
1 parent 32fd323 commit 4edb9f1

File tree

24 files changed

+430
-93
lines changed

24 files changed

+430
-93
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ jobs:
4141
matrix:
4242
CONNECT_VERSION:
4343
- preview
44+
- 2024.09.0
4445
- 2024.08.0
4546
- 2024.06.0
4647
- 2024.05.0

integration/Makefile

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,34 @@ CONNECT_BOOTSTRAP_SECRETKEY ?= $(shell head -c 32 /dev/random | base64)
1313
.PHONY: $(CONNECT_VERSIONS) \
1414
all \
1515
build \
16-
down \
17-
down-% \
18-
latest \
19-
test \
20-
up \
21-
up-% \
22-
help
16+
down \
17+
down-% \
18+
latest \
19+
test \
20+
up \
21+
up-% \
22+
help
2323

2424
# Versions
25-
CONNECT_VERSIONS := 2024.08.0 \
25+
CONNECT_VERSIONS := 2024.09.0 \
26+
2024.08.0 \
2627
2024.06.0 \
27-
2024.05.0 \
28-
2024.04.1 \
29-
2024.04.0 \
30-
2024.03.0 \
31-
2024.02.0 \
32-
2024.01.0 \
33-
2023.12.0 \
34-
2023.10.0 \
35-
2023.09.0 \
36-
2023.07.0 \
37-
2023.06.0 \
38-
2023.05.0 \
39-
2023.01.1 \
40-
2023.01.0 \
41-
2022.12.0 \
42-
2022.11.0
28+
2024.05.0 \
29+
2024.04.1 \
30+
2024.04.0 \
31+
2024.03.0 \
32+
2024.02.0 \
33+
2024.01.0 \
34+
2023.12.0 \
35+
2023.10.0 \
36+
2023.09.0 \
37+
2023.07.0 \
38+
2023.06.0 \
39+
2023.05.0 \
40+
2023.01.1 \
41+
2023.01.0 \
42+
2022.12.0 \
43+
2022.11.0
4344

4445
clean:
4546
rm -rf logs reports

integration/compose.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ services:
2121
- test
2222
connect:
2323
image: ${DOCKER_CONNECT_IMAGE}:${DOCKER_CONNECT_IMAGE_TAG}
24+
pull_policy: always
2425
environment:
2526
- CONNECT_BOOTSTRAP_ENABLED=true
2627
- CONNECT_BOOTSTRAP_SECRETKEY=${CONNECT_BOOTSTRAP_SECRETKEY}
28+
- CONNECT_APPLICATIONS_PACKAGEAUDITINGENABLED=true
2729
networks:
2830
- test
2931
privileged: true
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from packaging import version
1+
from packaging.version import parse
22

33
from posit import connect
44

55
client = connect.Client()
6-
client_version = client.version
7-
assert client_version is not None
8-
CONNECT_VERSION = version.parse(client_version)
6+
version = client.version
7+
assert version
8+
CONNECT_VERSION = parse(version)

integration/tests/posit/connect/test_jobs.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
from . import CONNECT_VERSION
99

1010

11+
@pytest.mark.skipif(
12+
CONNECT_VERSION <= version.parse("2023.01.1"),
13+
reason="Quarto not available",
14+
)
1115
class TestJobs:
1216
@classmethod
1317
def setup_class(cls):
@@ -19,10 +23,6 @@ def teardown_class(cls):
1923
cls.content.delete()
2024
assert cls.client.content.count() == 0
2125

22-
@pytest.mark.skipif(
23-
CONNECT_VERSION <= version.parse("2023.01.1"),
24-
reason="Quarto not available",
25-
)
2626
def test(self):
2727
content = self.content
2828

@@ -36,3 +36,18 @@ def test(self):
3636

3737
jobs = content.jobs
3838
assert len(jobs) == 1
39+
40+
def test_find_by(self):
41+
content = self.content
42+
43+
path = Path("../../../resources/connect/bundles/example-quarto-minimal/bundle.tar.gz")
44+
path = Path(__file__).parent / path
45+
path = path.resolve()
46+
path = str(path)
47+
48+
bundle = content.bundles.create(path)
49+
task = bundle.deploy()
50+
task.wait_for()
51+
52+
jobs = content.jobs
53+
assert len(jobs) != 0
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
from packaging import version
5+
6+
from posit import connect
7+
8+
from . import CONNECT_VERSION
9+
10+
11+
@pytest.mark.skipif(
12+
CONNECT_VERSION < version.parse("2024.10.0-dev"),
13+
reason="Packages API unavailable",
14+
)
15+
class TestPackages:
16+
@classmethod
17+
def setup_class(cls):
18+
cls.client = connect.Client()
19+
cls.content = cls.client.content.create(name=cls.__name__)
20+
path = Path("../../../resources/connect/bundles/example-flask-minimal/bundle.tar.gz")
21+
path = (Path(__file__).parent / path).resolve()
22+
bundle = cls.content.bundles.create(str(path))
23+
task = bundle.deploy()
24+
task.wait_for()
25+
26+
@classmethod
27+
def teardown_class(cls):
28+
cls.content.delete()
29+
30+
def test(self):
31+
assert self.client.packages
32+
assert self.content.packages
33+
34+
def test_find_by(self):
35+
package = self.client.packages.find_by(name="flask")
36+
assert package
37+
assert package["name"] == "flask"
38+
39+
package = self.content.packages.find_by(name="flask")
40+
assert package
41+
assert package["name"] == "flask"

src/posit/connect/client.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from .groups import Groups
1515
from .metrics import Metrics
1616
from .oauth import OAuth
17+
from .packages import Packages
1718
from .resources import ResourceParameters
1819
from .tasks import Tasks
1920
from .users import User, Users
@@ -155,7 +156,7 @@ def __init__(self, *args, **kwargs) -> None:
155156
session.hooks["response"].append(hooks.handle_errors)
156157
self.session = session
157158
self.resource_params = ResourceParameters(session, self.cfg.url)
158-
self.ctx = Context(self.session, self.cfg.url)
159+
self._ctx = Context(self.session, self.cfg.url)
159160

160161
@property
161162
def version(self) -> str | None:
@@ -167,7 +168,7 @@ def version(self) -> str | None:
167168
str
168169
The version of the Posit Connect server.
169170
"""
170-
return self.ctx.version
171+
return self._ctx.version
171172

172173
@property
173174
def me(self) -> User:
@@ -269,6 +270,11 @@ def oauth(self) -> OAuth:
269270
"""
270271
return OAuth(self.resource_params, self.cfg.api_key)
271272

273+
@property
274+
@requires(version="2024.10.0-dev")
275+
def packages(self) -> Packages:
276+
return Packages(self._ctx, "v1/packages")
277+
272278
@property
273279
def vanities(self) -> Vanities:
274280
return Vanities(self.resource_params)

src/posit/connect/content.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from .errors import ClientError
2525
from .jobs import JobsMixin
2626
from .oauth.associations import ContentItemAssociations
27+
from .packages import ContentPackagesMixin as PackagesMixin
2728
from .permissions import Permissions
2829
from .resources import Resource, ResourceParameters, Resources
2930
from .vanities import VanityMixin
@@ -171,7 +172,7 @@ class ContentItemOwner(Resource):
171172
pass
172173

173174

174-
class ContentItem(JobsMixin, VanityMixin, Resource):
175+
class ContentItem(JobsMixin, PackagesMixin, VanityMixin, Resource):
175176
class _AttrsBase(TypedDict, total=False):
176177
# # `name` will be set by other _Attrs classes
177178
# name: str

src/posit/connect/context.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def requires(version: str):
1111
def decorator(func):
1212
@functools.wraps(func)
1313
def wrapper(instance: ContextManager, *args, **kwargs):
14-
ctx = instance.ctx
14+
ctx = instance._ctx
1515
if ctx.version and Version(ctx.version) < Version(version):
1616
raise RuntimeError(
1717
f"This API is not available in Connect version {ctx.version}. Please upgrade to version {version} or later.",
@@ -45,4 +45,4 @@ def version(self, value):
4545

4646

4747
class ContextManager(Protocol):
48-
ctx: Context
48+
_ctx: Context

0 commit comments

Comments
 (0)