From 966f04c7ab399a2d72c89714cbd6a30a2a0e60cc Mon Sep 17 00:00:00 2001 From: tdstein Date: Mon, 1 Apr 2024 13:43:53 -0400 Subject: [PATCH] feat: adds create support for users --- src/posit/connect/users.py | 90 ++++++++++++++++++- .../connect/__api__/v1/users/remote.json | 20 +++++ tests/posit/connect/test_users.py | 52 +++++++++++ 3 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 tests/posit/connect/__api__/v1/users/remote.json diff --git a/src/posit/connect/users.py b/src/posit/connect/users.py index a35ce502..ecb25609 100644 --- a/src/posit/connect/users.py +++ b/src/posit/connect/users.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import List, overload +from typing import List, Optional, overload import requests @@ -210,8 +210,92 @@ def get(self, id: str) -> User: **response.json(), ) - def create(self) -> User: - raise NotImplementedError() + @overload + def create( + self, + username: str, + email: str = ..., + first_name: str = ..., + last_name: str = ..., + user_role: str = ..., + user_must_set_password: bool = ..., + password: str = ..., + unique_id: str = ..., + ) -> User: + """Creates a user. + + Creates a user from provided information (saml, pam, password, proxy, oauth2). + + Parameters + ---------- + username : str + email : str, optional + first_name : str, optional + last_name : str, optional + user_role : str, optional + user_must_set_password : bool, optional + password : str, optional + unique_id : str, optional + + Returns + ------- + User + The created user. + """ + ... + + @overload + def create(self, prefix: str) -> User: + """Creates a user. + + Creates a user via a remote authentication provider (ldap, google). + + Parameters + ---------- + prefix : str, optional + + Returns + ------- + User + The created user. + """ + ... + + @overload + def create(self, *args, **kwargs) -> User: + """Creates a user. + + Returns + ------- + User + The created user. + """ + ... + + def create(self, *args, **kwargs) -> User: + """Creates a user. + + Returns + ------- + User + The created user. + """ + body = dict(*args, **kwargs) + if "prefix" in body: + # Assume the remote flow if a 'prefix' is supplied + url = urls.append_path(self.config.url, "v1/users/remote") + paginator = Paginator(self.session, url, params=body) + results = paginator.fetch_results() + # TODO - Add assertion to verify result set includes a single user. + result = results[0] + url = urls.append_path(self.config.url, "v1/users") + ticket = result["temp_ticket"] + response = self.session.put(url, json={"temp_ticket": ticket}) + return User(config=self.config, session=self.session, **response.json()) + + url = urls.append_path(self.config.url, "v1/users") + response = self.session.post(url, json=body) + return User(config=self.config, session=self.session, **response.json()) def update(self) -> User: raise NotImplementedError() diff --git a/tests/posit/connect/__api__/v1/users/remote.json b/tests/posit/connect/__api__/v1/users/remote.json new file mode 100644 index 00000000..f0e658ec --- /dev/null +++ b/tests/posit/connect/__api__/v1/users/remote.json @@ -0,0 +1,20 @@ +{ + "results": [ + { + "email": "carlos@connect.example", + "username": "carlos12", + "first_name": "Carlos", + "last_name": "User", + "user_role": "publisher", + "created_time": "2019-09-09T15:24:32Z", + "updated_time": "2022-03-02T20:25:06Z", + "active_time": "2020-05-11T16:58:45Z", + "confirmed": true, + "locked": true, + "guid": "20a79ce3-6e87-4522-9faf-be24228800a4", + "temp_ticket": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + } + ], + "current_page": 1, + "total": 1 +} diff --git a/tests/posit/connect/test_users.py b/tests/posit/connect/test_users.py index b30ce4e6..15c578d5 100644 --- a/tests/posit/connect/test_users.py +++ b/tests/posit/connect/test_users.py @@ -274,6 +274,58 @@ def test_count(self): assert count == 3 +class TestUsersCreate: + @responses.activate + def test_with_prefix(self): + responses.get( + "https://connect.example/__api__/v1/users/remote", + json=load_mock("v1/users/remote.json"), + match=[ + responses.matchers.query_param_matcher( + { + "page_number": 1, + "page_size": 500, + "prefix": "carlos@connect.example", + } + ) + ], + ) + responses.put( + "https://connect.example/__api__/v1/users", + json=load_mock("v1/user.json"), + match=[ + responses.matchers.json_params_matcher( + { + "temp_ticket": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + } + ) + ], + ) + + con = Client(api_key="12345", url="https://connect.example/") + user = con.users.create(prefix="carlos@connect.example") + assert user.email == "carlos@connect.example" + + @responses.activate + def test(self): + params = { + "email": "carlos@connect.example", + "username": "carlos12", + "first_name": "Carlos", + "last_name": "User", + } + + responses.post( + "https://connect.example/__api__/v1/users", + json=load_mock("v1/user.json"), + match=[responses.matchers.json_params_matcher(params)], + ) + + con = Client(api_key="12345", url="https://connect.example/") + user = con.users.create(**params) + assert user.email == "carlos@connect.example" + + class TestUsersFindOne: @responses.activate def test_default(self):