diff --git a/src/posit/connect/client.py b/src/posit/connect/client.py index df0c1f15..507e4a78 100644 --- a/src/posit/connect/client.py +++ b/src/posit/connect/client.py @@ -3,7 +3,7 @@ from __future__ import annotations from requests import Response, Session -from typing import Optional +from typing import Optional, overload from . import hooks, me, urls @@ -47,11 +47,114 @@ class Client: Server version. """ - def __init__( - self, - api_key: Optional[str] = None, - url: Optional[str] = None, - ) -> None: + @overload + def __init__(self) -> None: + """Initialize a Client instance. + + Creates a client instance using credentials read from the environment. + + Environment Variables + --------------------- + CONNECT_SERVER - The Connect server URL. + CONNECT_API_KEY - The API key credential for client authentication. + + Examples + -------- + Client() + """ + ... + + @overload + def __init__(self, url: str) -> None: + """Initialize a Client instance. + + Creates a client instance using a provided URL and API key credential read from the environment. + + Environment Variables + --------------------- + CONNECT_API_KEY - The API key credential for client authentication. + + Parameters + ---------- + url : str + The Connect server URL. + + Examples + -------- + Client("https://connect.example.com) + """ + ... + + @overload + def __init__(self, url: str, api_key: str) -> None: + """Initialize a Client instance. + + Parameters + ---------- + url : str + The Connect server URL. + api_key : str + The API key credential for client authentication. + + Examples + -------- + >>> Client("https://connect.example.com", abcdefghijklmnopqrstuvwxyz012345") + """ + ... + + @overload + def __init__(self, *args, **kwargs) -> None: + """Initialize a Client instance.""" + ... + + def __init__(self, *args, **kwargs) -> None: + """Initialize a Client instance. + + Environment Variables + --------------------- + CONNECT_SERVER - The Connect server URL. + CONNECT_API_KEY - The API key credential for client authentication. + + Parameters + ---------- + *args + Variable length argument list. Can accept: + - (url: str) + url: str + The Connect server URL. + - (url: str, api_key: str) + url: str + The Connect server URL. + api_key: str + The API key credential for client authentication. + + **kwargs + Keyword arguments. Can include 'url' and 'api_key'. + + Examples + -------- + >>> Client() + >>> Client("https://connect.example.com") + >>> Client("https://connect.example.com", abcdefghijklmnopqrstuvwxyz012345") + >>> Client(api_key=""abcdefghijklmnopqrstuvwxyz012345", url="https://connect.example.com") + """ + api_key = None + url = None + if len(args) == 1 and isinstance(args[0], str): + url = args[0] + elif ( + len(args) == 2 + and isinstance(args[0], str) + and isinstance(args[1], str) + ): + url = args[0] + api_key = args[1] + else: + if "api_key" in kwargs and isinstance(kwargs["api_key"], str): + api_key = kwargs["api_key"] + if "url" in kwargs and isinstance(kwargs["url"], str): + url = kwargs["url"] + self.config = Config(api_key=api_key, url=url) session = Session() session.auth = Auth(config=self.config) diff --git a/tests/posit/connect/metrics/test_usage.py b/tests/posit/connect/metrics/test_usage.py index e056b513..3b5ef3c9 100644 --- a/tests/posit/connect/metrics/test_usage.py +++ b/tests/posit/connect/metrics/test_usage.py @@ -165,7 +165,7 @@ def test(self): ) # setup - c = connect.Client("12345", "https://connect.example") + c = connect.Client("https://connect.example", "12345") # invoke events = c.metrics.usage.find() @@ -239,7 +239,7 @@ def test(self): ) # setup - c = connect.Client("12345", "https://connect.example") + c = connect.Client("https://connect.example", "12345") # invoke view_event = c.metrics.usage.find_one() @@ -275,7 +275,7 @@ def test_none(self): ) # setup - c = connect.Client("12345", "https://connect.example") + c = connect.Client("https://connect.example", "12345") # invoke view_event = c.metrics.usage.find_one(content_guid="not-found") diff --git a/tests/posit/connect/test_bundles.py b/tests/posit/connect/test_bundles.py index 6f5b6269..2543a596 100644 --- a/tests/posit/connect/test_bundles.py +++ b/tests/posit/connect/test_bundles.py @@ -113,7 +113,7 @@ def test(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") bundle = c.content.get(content_guid).bundles.get(bundle_id) # invoke @@ -157,7 +157,7 @@ def test(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") bundle = c.content.get(content_guid).bundles.get(bundle_id) # invoke @@ -200,7 +200,7 @@ def test_output_as_str(self, mock_file: mock.MagicMock): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") bundle = c.content.get(content_guid).bundles.get(bundle_id) # invoke @@ -239,7 +239,7 @@ def test_output_as_io(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") bundle = c.content.get(content_guid).bundles.get(bundle_id) # invoke @@ -281,7 +281,7 @@ def test_invalid_arguments(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") bundle = c.content.get(content_guid).bundles.get(bundle_id) # invoke @@ -316,7 +316,7 @@ def test(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") content = c.content.get(content_guid) # invoke @@ -350,7 +350,7 @@ def test_kwargs_pathname(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") content = c.content.get(content_guid) # invoke @@ -373,7 +373,7 @@ def test_invalid_arguments(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") content = c.content.get(content_guid) # invoke @@ -398,7 +398,7 @@ def test(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") # invoke bundles = c.content.get(content_guid).bundles.find() @@ -427,7 +427,7 @@ def test(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") # invoke bundle = c.content.get(content_guid).bundles.find_one() @@ -458,7 +458,7 @@ def test(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") # invoke bundle = c.content.get(content_guid).bundles.get(bundle_id) diff --git a/tests/posit/connect/test_client.py b/tests/posit/connect/test_client.py index 51a5e85a..f563d591 100644 --- a/tests/posit/connect/test_client.py +++ b/tests/posit/connect/test_client.py @@ -26,6 +26,49 @@ def MockSession(): yield mock +class TestClientInit: + def test_no_arguments( + self, + MockAuth: MagicMock, + MockConfig: MagicMock, + MockSession: MagicMock, + ): + Client() + MockConfig.assert_called_once_with(api_key=None, url=None) + + def test_one_argument( + self, + MockAuth: MagicMock, + MockConfig: MagicMock, + MockSession: MagicMock, + ): + url = "https://connect.example.com" + Client(url) + MockConfig.assert_called_once_with(api_key=None, url=url) + + def test_two_arguments( + self, + MockAuth: MagicMock, + MockConfig: MagicMock, + MockSession: MagicMock, + ): + url = "https://connect.example.com" + api_key = "12345" + Client(url, api_key) + MockConfig.assert_called_once_with(api_key=api_key, url=url) + + def test_keyword_arguments( + self, + MockAuth: MagicMock, + MockConfig: MagicMock, + MockSession: MagicMock, + ): + url = "https://connect.example.com" + api_key = "12345" + Client(api_key=api_key, url=url) + MockConfig.assert_called_once_with(api_key=api_key, url=url) + + class TestClient: def test_init( self, @@ -33,45 +76,45 @@ def test_init( MockConfig: MagicMock, MockSession: MagicMock, ): - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" Client(api_key=api_key, url=url) MockAuth.assert_called_once_with(config=MockConfig.return_value) MockConfig.assert_called_once_with(api_key=api_key, url=url) MockSession.assert_called_once() def test__del__(self, MockAuth, MockConfig, MockSession): - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" client = Client(api_key=api_key, url=url) del client MockSession.return_value.close.assert_called_once() def test__enter__(self): - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" with Client(api_key=api_key, url=url) as client: assert isinstance(client, Client) def test__exit__(self, MockSession): - api_key = "foobar" - url = "http://foo.bar/__api__" - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" + api_key = "12345" + url = "https://connect.example.com" with Client(api_key=api_key, url=url) as client: assert isinstance(client, Client) MockSession.return_value.close.assert_called_once() @responses.activate def test_connect_version(self): - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" client = Client(api_key=api_key, url=url) # The actual server_settings response has a lot more attributes, but we # don't need to include them all here because we don't use them responses.get( - "http://foo.bar/__api__/server_settings", + "https://connect.example.com/__api__/server_settings", json={"version": "2024.01.0"}, ) assert client.version == "2024.01.0" @@ -87,52 +130,55 @@ def test_me_request(self): assert con.me.username == "carlos12" def test_request(self, MockSession): - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.request("GET", "/foo") MockSession.return_value.request.assert_called_once_with( - "GET", "http://foo.bar/__api__/foo" + "GET", "https://connect.example.com/__api__/foo" ) def test_get(self, MockSession): - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.get("/foo") client.session.get.assert_called_once_with( - "http://foo.bar/__api__/foo" + "https://connect.example.com/__api__/foo" ) def test_post(self, MockSession): - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.post("/foo") client.session.post.assert_called_once_with( - "http://foo.bar/__api__/foo" + "https://connect.example.com/__api__/foo" ) def test_put(self, MockSession): - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.put("/foo") client.session.put.assert_called_once_with( - "http://foo.bar/__api__/foo" + "https://connect.example.com/__api__/foo" ) def test_patch(self, MockSession): - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.patch("/foo") client.session.patch.assert_called_once_with( - "http://foo.bar/__api__/foo" + "https://connect.example.com/__api__/foo" ) def test_delete(self, MockSession): - api_key = "foobar" - url = "http://foo.bar/__api__" + api_key = "12345" + url = "https://connect.example.com" client = Client(api_key=api_key, url=url) client.delete("/foo") + client.session.delete.assert_called_once_with( + "https://connect.example.com/__api__/foo" + ) diff --git a/tests/posit/connect/test_content.py b/tests/posit/connect/test_content.py index cd71ddba..a0570539 100644 --- a/tests/posit/connect/test_content.py +++ b/tests/posit/connect/test_content.py @@ -205,7 +205,7 @@ def test_owner(self): ), ) - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") item = c.content.get("f2f37341-e21d-3d80-c698-a935ad614066") owner = item.owner assert owner.guid == "20a79ce3-6e87-4522-9faf-be24228800a4" @@ -264,7 +264,7 @@ def test(self): ) # setup - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") content = c.content.get(content_guid) # invoke @@ -285,7 +285,7 @@ def test_update(self): f"https://connect.example/__api__/v1/content/{guid}", json=load_mock(f"v1/content/{guid}.json"), ) - con = Client("12345", "https://connect.example") + con = Client("https://connect.example", "12345") content = con.content.get(guid) assert content.guid == guid @@ -340,7 +340,7 @@ def test(self): ) # setup - client = Client("12345", "https://connect.example") + client = Client("https://connect.example", "12345") # invoke content = client.content.find() @@ -362,7 +362,7 @@ def test_params_include(self): ) # setup - client = Client("12345", "https://connect.example") + client = Client("https://connect.example", "12345") # invoke client.content.find(include="tags") @@ -380,7 +380,7 @@ def test_params_include_none(self): ) # setup - client = Client("12345", "https://connect.example") + client = Client("https://connect.example", "12345") # invoke client.content.find(include=None) @@ -400,7 +400,7 @@ def test(self): ) # setup - client = Client("12345", "https://connect.example") + client = Client("https://connect.example", "12345") # invoke content_item = client.content.find_one() @@ -426,7 +426,7 @@ def test_owner_guid(self): ) # setup - client = Client("12345", "https://connect.example") + client = Client("https://connect.example", "12345") # invoke content_item = client.content.find_one(owner_guid=owner_guid) @@ -453,7 +453,7 @@ def test_name(self): ) # setup - client = Client("12345", "https://connect.example") + client = Client("https://connect.example", "12345") # invoke content_item = client.content.find_one(name=name) @@ -473,7 +473,7 @@ def test_params_include(self): ) # setup - client = Client("12345", "https://connect.example") + client = Client("https://connect.example", "12345") # invoke client.content.find_one(include="tags") @@ -491,7 +491,7 @@ def test_params_include_none(self): ) # setup - client = Client("12345", "https://connect.example") + client = Client("https://connect.example", "12345") # invoke client.content.find_one(include=None) @@ -509,7 +509,7 @@ def test(self): "v1/content/f2f37341-e21d-3d80-c698-a935ad614066.json" ), ) - con = Client("12345", "https://connect.example") + con = Client("https://connect.example", "12345") get_one = con.content.get("f2f37341-e21d-3d80-c698-a935ad614066") assert get_one.name == "Performance-Data-1671216053560" diff --git a/tests/posit/connect/test_hooks.py b/tests/posit/connect/test_hooks.py index 00c19c54..305557a2 100644 --- a/tests/posit/connect/test_hooks.py +++ b/tests/posit/connect/test_hooks.py @@ -74,7 +74,7 @@ def test_deprecation_warning(): "https://connect.example/__api__/v0", headers={"X-Deprecated-Endpoint": "v1/"}, ) - c = Client("12345", "https://connect.example") + c = Client("https://connect.example", "12345") with pytest.warns(DeprecationWarning): c.get("v0") diff --git a/tests/posit/connect/test_tasks.py b/tests/posit/connect/test_tasks.py index a58eb85a..a22a5e9c 100644 --- a/tests/posit/connect/test_tasks.py +++ b/tests/posit/connect/test_tasks.py @@ -60,7 +60,7 @@ def test(self): ) # setup - c = connect.Client("12345", "https://connect.example") + c = connect.Client("https://connect.example", "12345") task = c.tasks.get(id) assert not task.is_finished @@ -91,7 +91,7 @@ def test_with_params(self): ) # setup - c = connect.Client("12345", "https://connect.example") + c = connect.Client("https://connect.example", "12345") task = c.tasks.get(id) assert not task.is_finished @@ -122,7 +122,7 @@ def test(self): ) # setup - c = connect.Client("12345", "https://connect.example") + c = connect.Client("https://connect.example", "12345") task = c.tasks.get(id) assert not task.is_finished @@ -147,7 +147,7 @@ def test(self): ) # setup - c = connect.Client("12345", "https://connect.example") + c = connect.Client("https://connect.example", "12345") # invoke task = c.tasks.get(id)