diff --git a/ActiveCollabAPI/AcAccount.py b/ActiveCollabAPI/AcAccount.py index 6e691e8..99de25b 100644 --- a/ActiveCollabAPI/AcAccount.py +++ b/ActiveCollabAPI/AcAccount.py @@ -1,4 +1,5 @@ import dataclasses +import json from dataclasses import dataclass @@ -12,13 +13,13 @@ class AcAccount: class_: str status: str - def to_dict(self): + def to_dict(self) -> dict: d = dataclasses.asdict(self) d["class"] = d["class_"] del d["class_"] return d - def to_json(self): + def to_json(self) -> json: return json.dumps(self.to_dict()) def account_from_json(a) -> AcAccount: diff --git a/ActiveCollabAPI/ACAuthenticator.py b/ActiveCollabAPI/AcAuthenticator.py similarity index 96% rename from ActiveCollabAPI/ACAuthenticator.py rename to ActiveCollabAPI/AcAuthenticator.py index 6dcd140..76a9bee 100644 --- a/ActiveCollabAPI/ACAuthenticator.py +++ b/ActiveCollabAPI/AcAuthenticator.py @@ -8,7 +8,7 @@ from requests import Response from ActiveCollabAPI import AC_USER_AGENT -class ACAuthenticator: +class AcAuthenticator: base_url: str = "" diff --git a/ActiveCollabAPI/ACClient.py b/ActiveCollabAPI/AcClient.py similarity index 98% rename from ActiveCollabAPI/ACClient.py rename to ActiveCollabAPI/AcClient.py index a83ce27..49d927f 100644 --- a/ActiveCollabAPI/ACClient.py +++ b/ActiveCollabAPI/AcClient.py @@ -8,7 +8,10 @@ from ActiveCollabAPI.AcSubtask import AcSubtask from ActiveCollabAPI.AcToken import AcToken -class ACClient: +class AcClient: + """ + Active Collab REST API Client + """ base_url = None account = None token = None diff --git a/ActiveCollabAPI/AcProject.py b/ActiveCollabAPI/AcProject.py index 07b91ce..9366ada 100644 --- a/ActiveCollabAPI/AcProject.py +++ b/ActiveCollabAPI/AcProject.py @@ -49,13 +49,13 @@ class AcProject: updated_on: int url_path: str - def to_dict(self): + def to_dict(self) -> dict: d = dataclasses.asdict(self) d["class"] = d["class_"] del d["class_"] return d - def to_json(self): + def to_json(self) -> str: return json.dumps(self.to_dict()) diff --git a/ActiveCollabAPI/AcSession.py b/ActiveCollabAPI/AcSession.py index 32b707c..60a163c 100644 --- a/ActiveCollabAPI/AcSession.py +++ b/ActiveCollabAPI/AcSession.py @@ -14,10 +14,10 @@ class AcSession: cur_account: AcAccount token: AcToken - def to_dict(self): + def to_dict(self) -> dict: d = dataclasses.asdict(self) d["accounts"] = list(map(lambda a: a.to_dict(), self.accounts)) return d - def to_json(self): + def to_json(self) -> str: return json.dumps(self.to_dict()) \ No newline at end of file diff --git a/ActiveCollabAPI/AcSubtask.py b/ActiveCollabAPI/AcSubtask.py index 6fcbbf5..bbc5b26 100644 --- a/ActiveCollabAPI/AcSubtask.py +++ b/ActiveCollabAPI/AcSubtask.py @@ -30,13 +30,13 @@ class AcSubtask: position: int = 0 completed_on: int = 0 - def to_dict(self): + def to_dict(self) -> dict: d = dataclasses.asdict(self) d["class"] = d["class_"] del d["class_"] return d - def to_json(self): + def to_json(self) -> str: return json.dumps(self.to_dict()) diff --git a/ActiveCollabAPI/ACTokenAuthenticator.py b/ActiveCollabAPI/AcTokenAuthenticator.py similarity index 96% rename from ActiveCollabAPI/ACTokenAuthenticator.py rename to ActiveCollabAPI/AcTokenAuthenticator.py index dd52398..1d41022 100644 --- a/ActiveCollabAPI/ACTokenAuthenticator.py +++ b/ActiveCollabAPI/AcTokenAuthenticator.py @@ -6,7 +6,7 @@ from requests import Response from ActiveCollabAPI import AC_USER_AGENT, AC_API_VERSION, AC_API_CLIENT_NAME, AC_API_CLIENT_VENDOR -class ACTokenAuthenticator: +class AcTokenAuthenticator: base_url = "" diff --git a/ActiveCollabAPI/AcUser.py b/ActiveCollabAPI/AcUser.py index 6e15351..214be3b 100644 --- a/ActiveCollabAPI/AcUser.py +++ b/ActiveCollabAPI/AcUser.py @@ -10,9 +10,9 @@ class AcUser: last_name: str intent: str - def to_dict(self): + def to_dict(self) -> dict: d = dataclasses.asdict(self) return d - def to_json(self): - return json.dumps(self.to_dict()) \ No newline at end of file + def to_json(self) -> str: + return json.dumps(self.to_dict()) diff --git a/ActiveCollabAPI/ActiveCollab.py b/ActiveCollabAPI/ActiveCollab.py index b4323a5..68c2aa0 100644 --- a/ActiveCollabAPI/ActiveCollab.py +++ b/ActiveCollabAPI/ActiveCollab.py @@ -1,10 +1,8 @@ -from dataclasses import dataclass -from pprint import pprint from ActiveCollabAPI import AC_API_VERSION -from ActiveCollabAPI.ACAuthenticator import ACAuthenticator -from ActiveCollabAPI.ACClient import ACClient -from ActiveCollabAPI.ACTokenAuthenticator import ACTokenAuthenticator +from ActiveCollabAPI.AcAuthenticator import AcAuthenticator +from ActiveCollabAPI.AcClient import AcClient +from ActiveCollabAPI.AcTokenAuthenticator import AcTokenAuthenticator from ActiveCollabAPI.AcAccount import AcAccount, account_from_json from ActiveCollabAPI.AcLoginResponse import AcLoginResponse from ActiveCollabAPI.AcProject import AcProject, project_from_json @@ -15,6 +13,9 @@ from ActiveCollabAPI.AcUser import AcUser class ActiveCollab: + """ + Active Collab Client library comming from the use case + """ base_url: str = "" session: AcSession = None @@ -30,7 +31,7 @@ class ActiveCollab: return self.session def user_login(self, email: str, password: str) -> AcLoginResponse: - auth = ACAuthenticator(self.base_url) + auth = AcAuthenticator(self.base_url) res = auth.login(email, password) if res.status_code != 200: raise Exception('Login failed!') @@ -44,7 +45,7 @@ class ActiveCollab: return accounts[0] def create_token(self, account: AcAccount, user: AcUser) -> AcToken: - authenticator = ACTokenAuthenticator(account.url + '/api/v%s' % AC_API_VERSION) + authenticator = AcTokenAuthenticator(account.url + '/api/v%s' % AC_API_VERSION) res = authenticator.issue_token_intent(user.intent) if res.status_code != 200: raise Exception('Request token failed!') @@ -54,12 +55,12 @@ class ActiveCollab: return AcToken(res_data["token"]) def get_info(self): - client = ACClient(self.session.cur_account, self.session.token) + client = AcClient(self.session.cur_account, self.session.token) res = client.get_info() return res.json() def get_projects(self) -> list[AcProject]: - client = ACClient(self.session.cur_account, self.session.token) + client = AcClient(self.session.cur_account, self.session.token) res = client.get_projects() if res.status_code != 200: raise Exception("Error %d" % res.status_code) @@ -68,14 +69,14 @@ class ActiveCollab: return projects def get_subtasks(self, project_id: int, task_id: int) -> list[AcSubtask]: - client = ACClient(self.session.cur_account, self.session.token) + client = AcClient(self.session.cur_account, self.session.token) res = client.get_project_task(project_id, task_id) res_data = res.json() subtasks = list(map(lambda sub: subtask_from_json(sub), res_data["subtasks"])) return subtasks def create_subtask(self, subtask: AcSubtask): - client = ACClient(self.session.cur_account, self.session.token) + client = AcClient(self.session.cur_account, self.session.token) res = client.create_subtask(subtask) res_data = res.json() return res_data diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..29a0281 --- /dev/null +++ b/TODO.md @@ -0,0 +1,17 @@ + +## Backlog + +* Tests for CLI +* pack with https://python-poetry.org +* publish first version on github +* complete [API](https://developers.activecollab.com/api-documentation/index.html) + * Project list, get, create, delete + * Task list, get, create, delete + * Tasklist + * Subtask list, get, create, delete + * Attachments + * Companies + * Users + * Teams + * Notes +* support for paging diff --git a/tests/test_ACAuthenticator.py b/tests/test_AcAuthenticator.py similarity index 85% rename from tests/test_ACAuthenticator.py rename to tests/test_AcAuthenticator.py index 9bf9725..912e83b 100644 --- a/tests/test_ACAuthenticator.py +++ b/tests/test_AcAuthenticator.py @@ -3,10 +3,10 @@ from unittest import TestCase from unittest.mock import patch from ActiveCollabAPI import AC_LOGIN_BASE_URL -from ActiveCollabAPI.ACAuthenticator import ACAuthenticator +from ActiveCollabAPI.AcAuthenticator import AcAuthenticator -class TestACAuthenticator(TestCase): +class TestAcAuthenticator(TestCase): def mock_login_success(self): return { @@ -31,13 +31,13 @@ class TestACAuthenticator(TestCase): def test_login_success_gets_user_and_accounts_list(self): - with patch("ActiveCollabAPI.ACAuthenticator.requests.post") as mock_post: + with patch("ActiveCollabAPI.AcAuthenticator.requests.post") as mock_post: mock_post.return_value.status_code = 200 mock_post.return_value.json.return_value = self.mock_login_success() email = "collab@example.com" password = "VeryS3cret!" - authenticator = ACAuthenticator(AC_LOGIN_BASE_URL) + authenticator = AcAuthenticator(AC_LOGIN_BASE_URL) response = authenticator.login(email, password) self.assertEqual(200, response.status_code) @@ -57,13 +57,13 @@ class TestACAuthenticator(TestCase): } def test_login_failed_not_ok(self): - with patch("ActiveCollabAPI.ACAuthenticator.requests.post") as mock_post: + with patch("ActiveCollabAPI.AcAuthenticator.requests.post") as mock_post: mock_post.return_value.status_code = 200 mock_post.return_value.json.return_value = self.mock_login_failed() email = "collab@example.com" password = "VeryS3cret!" - authenticator = ACAuthenticator(AC_LOGIN_BASE_URL) + authenticator = AcAuthenticator(AC_LOGIN_BASE_URL) response = authenticator.login(email, password) self.assertEqual(200, response.status_code) @@ -75,13 +75,13 @@ class TestACAuthenticator(TestCase): return { } def test_login_forbidden(self): - with patch("ActiveCollabAPI.ACAuthenticator.requests.post") as mock_post: + with patch("ActiveCollabAPI.AcAuthenticator.requests.post") as mock_post: mock_post.return_value.status_code = 401 mock_post.return_value.json.return_value = self.mock_login_forbidden() email = "collab@example.com" password = "VeryS3cret!" - authenticator = ACAuthenticator(AC_LOGIN_BASE_URL) + authenticator = AcAuthenticator(AC_LOGIN_BASE_URL) response = authenticator.login(email, password) self.assertEqual(401, response.status_code) diff --git a/tests/test_ACTokenAuthenticator.py b/tests/test_AcTokenAuthenticator.py similarity index 76% rename from tests/test_ACTokenAuthenticator.py rename to tests/test_AcTokenAuthenticator.py index 3af05ef..c4dca12 100644 --- a/tests/test_ACTokenAuthenticator.py +++ b/tests/test_AcTokenAuthenticator.py @@ -1,10 +1,10 @@ from unittest import TestCase from unittest.mock import patch -from ActiveCollabAPI.ACTokenAuthenticator import ACTokenAuthenticator +from ActiveCollabAPI.AcTokenAuthenticator import AcTokenAuthenticator -class TestACTokenAuthenticator(TestCase): +class TestAcTokenAuthenticator(TestCase): def mock_token_success(self): return { @@ -13,11 +13,11 @@ class TestACTokenAuthenticator(TestCase): } def test_issue_token_intent_success(self): - with patch("ActiveCollabAPI.ACTokenAuthenticator.requests.post") as mock_post: + with patch("ActiveCollabAPI.AcTokenAuthenticator.requests.post") as mock_post: mock_post.return_value.status_code = 200 mock_post.return_value.json.return_value = self.mock_token_success() - auth = ACTokenAuthenticator("http://mock") + auth = AcTokenAuthenticator("http://mock") res = auth.issue_token_intent("this-is-the-token-intent-string") self.assertEqual(200, res.status_code) @@ -31,11 +31,11 @@ class TestACTokenAuthenticator(TestCase): } def test_issue_token_intent_not_ok(self): - with patch("ActiveCollabAPI.ACTokenAuthenticator.requests.post") as mock_post: + with patch("ActiveCollabAPI.AcTokenAuthenticator.requests.post") as mock_post: mock_post.return_value.status_code = 200 mock_post.return_value.json.return_value = self.mock_token_not_ok() - auth = ACTokenAuthenticator("http://mock") + auth = AcTokenAuthenticator("http://mock") res = auth.issue_token_intent("this-is-the-token-intent-string") self.assertEqual(200, res.status_code) @@ -47,11 +47,11 @@ class TestACTokenAuthenticator(TestCase): } def test_issue_token_intent_forbidden(self): - with patch("ActiveCollabAPI.ACTokenAuthenticator.requests.post") as mock_post: + with patch("ActiveCollabAPI.AcTokenAuthenticator.requests.post") as mock_post: mock_post.return_value.status_code = 401 mock_post.return_value.json.return_value = self.mock_token_forbidden() - auth = ACTokenAuthenticator("http://mock") + auth = AcTokenAuthenticator("http://mock") res = auth.issue_token_intent("this-is-the-token-intent-string") self.assertEqual(401, res.status_code)