Cleanup some code
NOT FINISHED!
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import dataclasses
|
import dataclasses
|
||||||
|
import json
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
@@ -12,13 +13,13 @@ class AcAccount:
|
|||||||
class_: str
|
class_: str
|
||||||
status: str
|
status: str
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self) -> dict:
|
||||||
d = dataclasses.asdict(self)
|
d = dataclasses.asdict(self)
|
||||||
d["class"] = d["class_"]
|
d["class"] = d["class_"]
|
||||||
del d["class_"]
|
del d["class_"]
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self) -> json:
|
||||||
return json.dumps(self.to_dict())
|
return json.dumps(self.to_dict())
|
||||||
|
|
||||||
def account_from_json(a) -> AcAccount:
|
def account_from_json(a) -> AcAccount:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from requests import Response
|
|||||||
from ActiveCollabAPI import AC_USER_AGENT
|
from ActiveCollabAPI import AC_USER_AGENT
|
||||||
|
|
||||||
|
|
||||||
class ACAuthenticator:
|
class AcAuthenticator:
|
||||||
|
|
||||||
base_url: str = ""
|
base_url: str = ""
|
||||||
|
|
||||||
@@ -8,7 +8,10 @@ from ActiveCollabAPI.AcSubtask import AcSubtask
|
|||||||
from ActiveCollabAPI.AcToken import AcToken
|
from ActiveCollabAPI.AcToken import AcToken
|
||||||
|
|
||||||
|
|
||||||
class ACClient:
|
class AcClient:
|
||||||
|
"""
|
||||||
|
Active Collab REST API Client
|
||||||
|
"""
|
||||||
base_url = None
|
base_url = None
|
||||||
account = None
|
account = None
|
||||||
token = None
|
token = None
|
||||||
@@ -49,13 +49,13 @@ class AcProject:
|
|||||||
updated_on: int
|
updated_on: int
|
||||||
url_path: str
|
url_path: str
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self) -> dict:
|
||||||
d = dataclasses.asdict(self)
|
d = dataclasses.asdict(self)
|
||||||
d["class"] = d["class_"]
|
d["class"] = d["class_"]
|
||||||
del d["class_"]
|
del d["class_"]
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self) -> str:
|
||||||
return json.dumps(self.to_dict())
|
return json.dumps(self.to_dict())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ class AcSession:
|
|||||||
cur_account: AcAccount
|
cur_account: AcAccount
|
||||||
token: AcToken
|
token: AcToken
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self) -> dict:
|
||||||
d = dataclasses.asdict(self)
|
d = dataclasses.asdict(self)
|
||||||
d["accounts"] = list(map(lambda a: a.to_dict(), self.accounts))
|
d["accounts"] = list(map(lambda a: a.to_dict(), self.accounts))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self) -> str:
|
||||||
return json.dumps(self.to_dict())
|
return json.dumps(self.to_dict())
|
||||||
@@ -30,13 +30,13 @@ class AcSubtask:
|
|||||||
position: int = 0
|
position: int = 0
|
||||||
completed_on: int = 0
|
completed_on: int = 0
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self) -> dict:
|
||||||
d = dataclasses.asdict(self)
|
d = dataclasses.asdict(self)
|
||||||
d["class"] = d["class_"]
|
d["class"] = d["class_"]
|
||||||
del d["class_"]
|
del d["class_"]
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self) -> str:
|
||||||
return json.dumps(self.to_dict())
|
return json.dumps(self.to_dict())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
from ActiveCollabAPI import AC_USER_AGENT, AC_API_VERSION, AC_API_CLIENT_NAME, AC_API_CLIENT_VENDOR
|
||||||
|
|
||||||
|
|
||||||
class ACTokenAuthenticator:
|
class AcTokenAuthenticator:
|
||||||
|
|
||||||
base_url = ""
|
base_url = ""
|
||||||
|
|
||||||
@@ -10,9 +10,9 @@ class AcUser:
|
|||||||
last_name: str
|
last_name: str
|
||||||
intent: str
|
intent: str
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self) -> dict:
|
||||||
d = dataclasses.asdict(self)
|
d = dataclasses.asdict(self)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self) -> str:
|
||||||
return json.dumps(self.to_dict())
|
return json.dumps(self.to_dict())
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
from dataclasses import dataclass
|
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
from ActiveCollabAPI import AC_API_VERSION
|
from ActiveCollabAPI import AC_API_VERSION
|
||||||
from ActiveCollabAPI.ACAuthenticator import ACAuthenticator
|
from ActiveCollabAPI.AcAuthenticator import AcAuthenticator
|
||||||
from ActiveCollabAPI.ACClient import ACClient
|
from ActiveCollabAPI.AcClient import AcClient
|
||||||
from ActiveCollabAPI.ACTokenAuthenticator import ACTokenAuthenticator
|
from ActiveCollabAPI.AcTokenAuthenticator import AcTokenAuthenticator
|
||||||
from ActiveCollabAPI.AcAccount import AcAccount, account_from_json
|
from ActiveCollabAPI.AcAccount import AcAccount, account_from_json
|
||||||
from ActiveCollabAPI.AcLoginResponse import AcLoginResponse
|
from ActiveCollabAPI.AcLoginResponse import AcLoginResponse
|
||||||
from ActiveCollabAPI.AcProject import AcProject, project_from_json
|
from ActiveCollabAPI.AcProject import AcProject, project_from_json
|
||||||
@@ -15,6 +13,9 @@ from ActiveCollabAPI.AcUser import AcUser
|
|||||||
|
|
||||||
|
|
||||||
class ActiveCollab:
|
class ActiveCollab:
|
||||||
|
"""
|
||||||
|
Active Collab Client library comming from the use case
|
||||||
|
"""
|
||||||
base_url: str = ""
|
base_url: str = ""
|
||||||
|
|
||||||
session: AcSession = None
|
session: AcSession = None
|
||||||
@@ -30,7 +31,7 @@ class ActiveCollab:
|
|||||||
return self.session
|
return self.session
|
||||||
|
|
||||||
def user_login(self, email: str, password: str) -> AcLoginResponse:
|
def user_login(self, email: str, password: str) -> AcLoginResponse:
|
||||||
auth = ACAuthenticator(self.base_url)
|
auth = AcAuthenticator(self.base_url)
|
||||||
res = auth.login(email, password)
|
res = auth.login(email, password)
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
raise Exception('Login failed!')
|
raise Exception('Login failed!')
|
||||||
@@ -44,7 +45,7 @@ class ActiveCollab:
|
|||||||
return accounts[0]
|
return accounts[0]
|
||||||
|
|
||||||
def create_token(self, account: AcAccount, user: AcUser) -> AcToken:
|
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)
|
res = authenticator.issue_token_intent(user.intent)
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
raise Exception('Request token failed!')
|
raise Exception('Request token failed!')
|
||||||
@@ -54,12 +55,12 @@ class ActiveCollab:
|
|||||||
return AcToken(res_data["token"])
|
return AcToken(res_data["token"])
|
||||||
|
|
||||||
def get_info(self):
|
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()
|
res = client.get_info()
|
||||||
return res.json()
|
return res.json()
|
||||||
|
|
||||||
def get_projects(self) -> list[AcProject]:
|
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()
|
res = client.get_projects()
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
raise Exception("Error %d" % res.status_code)
|
raise Exception("Error %d" % res.status_code)
|
||||||
@@ -68,14 +69,14 @@ class ActiveCollab:
|
|||||||
return projects
|
return projects
|
||||||
|
|
||||||
def get_subtasks(self, project_id: int, task_id: int) -> list[AcSubtask]:
|
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 = client.get_project_task(project_id, task_id)
|
||||||
res_data = res.json()
|
res_data = res.json()
|
||||||
subtasks = list(map(lambda sub: subtask_from_json(sub), res_data["subtasks"]))
|
subtasks = list(map(lambda sub: subtask_from_json(sub), res_data["subtasks"]))
|
||||||
return subtasks
|
return subtasks
|
||||||
|
|
||||||
def create_subtask(self, subtask: AcSubtask):
|
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 = client.create_subtask(subtask)
|
||||||
res_data = res.json()
|
res_data = res.json()
|
||||||
return res_data
|
return res_data
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -3,10 +3,10 @@ from unittest import TestCase
|
|||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from ActiveCollabAPI import AC_LOGIN_BASE_URL
|
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):
|
def mock_login_success(self):
|
||||||
return {
|
return {
|
||||||
@@ -31,13 +31,13 @@ class TestACAuthenticator(TestCase):
|
|||||||
|
|
||||||
|
|
||||||
def test_login_success_gets_user_and_accounts_list(self):
|
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.status_code = 200
|
||||||
mock_post.return_value.json.return_value = self.mock_login_success()
|
mock_post.return_value.json.return_value = self.mock_login_success()
|
||||||
|
|
||||||
email = "collab@example.com"
|
email = "collab@example.com"
|
||||||
password = "VeryS3cret!"
|
password = "VeryS3cret!"
|
||||||
authenticator = ACAuthenticator(AC_LOGIN_BASE_URL)
|
authenticator = AcAuthenticator(AC_LOGIN_BASE_URL)
|
||||||
response = authenticator.login(email, password)
|
response = authenticator.login(email, password)
|
||||||
|
|
||||||
self.assertEqual(200, response.status_code)
|
self.assertEqual(200, response.status_code)
|
||||||
@@ -57,13 +57,13 @@ class TestACAuthenticator(TestCase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def test_login_failed_not_ok(self):
|
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.status_code = 200
|
||||||
mock_post.return_value.json.return_value = self.mock_login_failed()
|
mock_post.return_value.json.return_value = self.mock_login_failed()
|
||||||
|
|
||||||
email = "collab@example.com"
|
email = "collab@example.com"
|
||||||
password = "VeryS3cret!"
|
password = "VeryS3cret!"
|
||||||
authenticator = ACAuthenticator(AC_LOGIN_BASE_URL)
|
authenticator = AcAuthenticator(AC_LOGIN_BASE_URL)
|
||||||
response = authenticator.login(email, password)
|
response = authenticator.login(email, password)
|
||||||
|
|
||||||
self.assertEqual(200, response.status_code)
|
self.assertEqual(200, response.status_code)
|
||||||
@@ -75,13 +75,13 @@ class TestACAuthenticator(TestCase):
|
|||||||
return {
|
return {
|
||||||
}
|
}
|
||||||
def test_login_forbidden(self):
|
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.status_code = 401
|
||||||
mock_post.return_value.json.return_value = self.mock_login_forbidden()
|
mock_post.return_value.json.return_value = self.mock_login_forbidden()
|
||||||
|
|
||||||
email = "collab@example.com"
|
email = "collab@example.com"
|
||||||
password = "VeryS3cret!"
|
password = "VeryS3cret!"
|
||||||
authenticator = ACAuthenticator(AC_LOGIN_BASE_URL)
|
authenticator = AcAuthenticator(AC_LOGIN_BASE_URL)
|
||||||
response = authenticator.login(email, password)
|
response = authenticator.login(email, password)
|
||||||
|
|
||||||
self.assertEqual(401, response.status_code)
|
self.assertEqual(401, response.status_code)
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from unittest.mock import patch
|
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):
|
def mock_token_success(self):
|
||||||
return {
|
return {
|
||||||
@@ -13,11 +13,11 @@ class TestACTokenAuthenticator(TestCase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def test_issue_token_intent_success(self):
|
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.status_code = 200
|
||||||
mock_post.return_value.json.return_value = self.mock_token_success()
|
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")
|
res = auth.issue_token_intent("this-is-the-token-intent-string")
|
||||||
|
|
||||||
self.assertEqual(200, res.status_code)
|
self.assertEqual(200, res.status_code)
|
||||||
@@ -31,11 +31,11 @@ class TestACTokenAuthenticator(TestCase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def test_issue_token_intent_not_ok(self):
|
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.status_code = 200
|
||||||
mock_post.return_value.json.return_value = self.mock_token_not_ok()
|
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")
|
res = auth.issue_token_intent("this-is-the-token-intent-string")
|
||||||
|
|
||||||
self.assertEqual(200, res.status_code)
|
self.assertEqual(200, res.status_code)
|
||||||
@@ -47,11 +47,11 @@ class TestACTokenAuthenticator(TestCase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def test_issue_token_intent_forbidden(self):
|
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.status_code = 401
|
||||||
mock_post.return_value.json.return_value = self.mock_token_forbidden()
|
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")
|
res = auth.issue_token_intent("this-is-the-token-intent-string")
|
||||||
|
|
||||||
self.assertEqual(401, res.status_code)
|
self.assertEqual(401, res.status_code)
|
||||||
Reference in New Issue
Block a user