Skip to content

Commit

Permalink
fix: Support login() method
Browse files Browse the repository at this point in the history
  • Loading branch information
fundthmcalculus committed Sep 23, 2024
1 parent e910de6 commit 1c15da4
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 12 deletions.
36 changes: 30 additions & 6 deletions src/pyconnectwise/clients/change_request_client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import base64
import typing
from datetime import datetime, timedelta

from pyconnectwise.clients.connectwise_client import ConnectWiseClient
from pyconnectwise.config import Config
Expand Down Expand Up @@ -27,8 +28,10 @@ def __init__(
client_id: str,
member_hash: str,
member_id: str,
token: str,
change_approval_cookie: str,
login_username: str,
login_password: str,
login_role: str,
login_partner_id: str,
config: Config | None = None,
) -> None:
"""
Expand All @@ -43,13 +46,17 @@ def __init__(
private_key (str): Your ConnectWise Manage API Private key.
config (Config, optional): Optional additional configuration for API interactions
"""
self.login_partner_id: str = login_partner_id
self.login_role: str = login_role
self.login_password: str = login_password
self.login_username: str = login_username
self.client_id: str = client_id
self.company_name: str = company_name
self.manage_url: str = manage_url
self.member_hash: str = member_hash
self.member_id: str = member_id
self.token: str = token
self.change_approval_cookie: str = change_approval_cookie
self.__change_approval_cookie: str = ""
self.__change_approval_cookie_expiration: datetime | None = None

if config:
self.config = config
Expand Down Expand Up @@ -97,6 +104,23 @@ def audit_log(self):
# Yes, this has a different route than the plural version
raise NotImplementedError("audit log singular endpoint not implemented yet.")

def login(self) -> None:
"""
Logs in to the ConnectWise Change Approval API and retrieves the new cookie
"""
url = f"https://{self.manage_url}/api/login"
result = self._make_request("POST", url, data={
"userName": self.login_username,
"password": self.login_password,
"role": self.login_role,
"partnerId": self.login_partner_id,
"cwversion": "2024.1", # TODO - Parameterize?
"context": "web",
})
result.raise_for_status()
self.__change_approval_cookie = result.cookies["changeapproval"]
# TODO - We get back expires=None, so log in every 6 hours?
self.__change_approval_cookie_expiration = datetime.now() + timedelta(hours=6)

def _get_cookies(self) -> dict[str, str]:
"""
Expand All @@ -105,12 +129,12 @@ def _get_cookies(self) -> dict[str, str]:
Returns:
dict[str, str]: Dictionary of cookies.
"""

return {
"companyName": self.company_name,
"MemberID": self.member_id,
"MemberHash": self.member_hash,
"Token": self.token,
"changeapproval": self.change_approval_cookie,
"changeapproval": self.__change_approval_cookie,
}

def _get_url(self) -> str:
Expand Down
7 changes: 6 additions & 1 deletion src/pyconnectwise/clients/connectwise_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def _get_headers(self) -> dict[str, str]:

@abstractmethod
def _get_cookies(self) -> dict[str, str]:
pass
return {}

@abstractmethod
def _get_url(self) -> str:
Expand Down Expand Up @@ -116,6 +116,11 @@ def _make_request( # noqa: C901
if response.status_code == 400:
raise MalformedRequestException(response)
if response.status_code == 401:
# TODO - Figure out a better way to retry on 401 if an authorization cookie is provided
if retry_count < self.config.max_retries and "changeapproval" in cookies:
cookies["changeapproval"] = response.cookies["changeapproval"]
retry_count += 1
return self._make_request(method, url, data, params, headers, retry_count, cookies=cookies)
raise AuthenticationFailedException(response)
if response.status_code == 403:
raise PermissionsFailedException(response)
Expand Down
14 changes: 9 additions & 5 deletions tests/clients/test_change_approvals.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ def change_approval_client_init():
client_id=os.environ["CW_CLIENT_ID"],
member_hash=os.environ["CW_MEMBER_HASH"],
member_id=os.environ["CW_MEMBER_ID"],
token=os.environ["CW_TOKEN"],
change_approval_cookie=os.environ["CW_CHANGE_APPROVAL_COOKIE"],
login_username=os.environ["CW_LOGIN_USERNAME"],
login_password=os.environ["CW_LOGIN_PASSWORD"],
login_role=os.environ["CW_LOGIN_ROLE"],
login_partner_id=os.environ["CW_LOGIN_PARTNER_ID"],
)
return client

Expand All @@ -30,16 +32,18 @@ def test_change_approval_client_init():
assert client.client_id == os.environ["CW_CLIENT_ID"]
assert client.member_hash == os.environ["CW_MEMBER_HASH"]
assert client.member_id == os.environ["CW_MEMBER_ID"]
assert client.token == os.environ["CW_TOKEN"]
assert client.change_approval_cookie == os.environ["CW_CHANGE_APPROVAL_COOKIE"]


def test_get_change_approval():
# Testing with real data. :D
change_request_id = "66ed7a101336c1045d07502f"
client = change_approval_client_init()
client.login()
change_approval = client.change_request.id(change_request_id).get()
assert change_approval.id == change_request_id



def test_login():
client = change_approval_client_init()
client.login()
assert True

0 comments on commit 1c15da4

Please sign in to comment.