improving editing api objects

pull/12/head
Langenfeld 2021-11-16 14:21:48 +01:00
rodzic d1430acf8a
commit 94261487cf
4 zmienionych plików z 100 dodań i 44 usunięć

Wyświetl plik

@ -31,13 +31,13 @@ class Organization(ApiObject):
return cls._request(gitea, {"name": name}) return cls._request(gitea, {"name": name})
@classmethod @classmethod
def parse_response(cls, gitea, result): def parse_response(cls, gitea, result) -> 'Organization':
api_object = super().parse_response(gitea, result) api_object = super().parse_response(gitea, result)
# add "name" field to make this behave similar to users # add "name" field to make this behave similar to users
Organization._add_read_property("name", result["username"], api_object) Organization._add_read_property("name", result["username"], api_object)
return api_object return api_object
patchable_fields = {"description", "full_name", "location", "visibility", "website"} _patchable_fields = {"description", "full_name", "location", "visibility", "website"}
def commit(self): def commit(self):
values = self.get_dirty_fields() values = self.get_dirty_fields()
@ -141,7 +141,7 @@ class User(ApiObject):
api_object = cls._request(gitea, {"name": name}) api_object = cls._request(gitea, {"name": name})
return api_object return api_object
patchable_fields = { _patchable_fields = {
"active", "active",
"admin", "admin",
"allow_create_organization", "allow_create_organization",
@ -231,7 +231,7 @@ class Branch(ReadonlyApiObject):
def __hash__(self): def __hash__(self):
return hash(self.commit["id"]) ^ hash(self.name) return hash(self.commit["id"]) ^ hash(self.name)
fields_to_parsers = { _fields_to_parsers = {
# This is not a commit object # This is not a commit object
#"commit": lambda gitea, c: Commit.parse_response(gitea, c) #"commit": lambda gitea, c: Commit.parse_response(gitea, c)
} }
@ -246,7 +246,7 @@ class Repository(ApiObject):
REPO_IS_COLLABORATOR = """/repos/%s/%s/collaborators/%s""" # <owner>, <reponame>, <username> REPO_IS_COLLABORATOR = """/repos/%s/%s/collaborators/%s""" # <owner>, <reponame>, <username>
REPO_SEARCH = """/repos/search/%s""" # <reponame> REPO_SEARCH = """/repos/search/%s""" # <reponame>
REPO_BRANCHES = """/repos/%s/%s/branches""" # <owner>, <reponame> REPO_BRANCHES = """/repos/%s/%s/branches""" # <owner>, <reponame>
REPO_ISSUES = """/repos/%s/%s/issues""" # <owner, reponame> REPO_ISSUES = """/repos/{owner}/{repo}/issues""" # <owner, reponame>
REPO_DELETE = """/repos/%s/%s""" # <owner>, <reponame> REPO_DELETE = """/repos/%s/%s""" # <owner>, <reponame>
REPO_TIMES = """/repos/%s/%s/times""" # <owner>, <reponame> REPO_TIMES = """/repos/%s/%s/times""" # <owner>, <reponame>
REPO_USER_TIME = """/repos/%s/%s/times/%s""" # <owner>, <reponame>, <username> REPO_USER_TIME = """/repos/%s/%s/times/%s""" # <owner>, <reponame>, <username>
@ -266,11 +266,10 @@ class Repository(ApiObject):
def __hash__(self): def __hash__(self):
return hash(self.owner) ^ hash(self.name) return hash(self.owner) ^ hash(self.name)
fields_to_parsers = { _fields_to_parsers = {
# dont know how to tell apart user and org as owner except form email being empty. # dont know how to tell apart user and org as owner except form email being empty.
"owner": lambda gitea, r: Organization.parse_response(gitea, r) "owner": lambda gitea, r: Organization.parse_response(gitea, r)
if r["email"] == "" if r["email"] == "" else User.parse_response(gitea, r),
else User.parse_response(gitea, r),
"updated_at": lambda gitea, t: Util.convert_time(t), "updated_at": lambda gitea, t: Util.convert_time(t),
} }
@ -278,7 +277,7 @@ class Repository(ApiObject):
def request(cls, gitea, owner, name): def request(cls, gitea, owner, name):
return cls._request(gitea, {"owner": owner, "name": name}) return cls._request(gitea, {"owner": owner, "name": name})
patchable_fields = { _patchable_fields = {
"allow_merge_commits", "allow_merge_commits",
"allow_rebase", "allow_rebase",
"allow_rebase_explicit", "allow_rebase_explicit",
@ -333,16 +332,15 @@ class Repository(ApiObject):
"""Get issues of state Issue.open or Issue.closed of a repository.""" """Get issues of state Issue.open or Issue.closed of a repository."""
assert state in [Issue.OPENED, Issue.CLOSED] assert state in [Issue.OPENED, Issue.CLOSED]
issues = [] issues = []
# "page": -1 is returning _all_ issues instead of pages. Hopefully this is intended behaviour. data = {"state": state}
data = {"page": -1, "state": state} results = self.gitea.requests_get_paginated(
results = self.gitea.requests_get( Repository.REPO_ISSUES.format(owner=self.owner.username, repo=self.name), params=data
Repository.REPO_ISSUES % (self.owner.username, self.name), params=data
) )
for result in results: for result in results:
issue = Issue.parse_response(self.gitea, result) issue = Issue.parse_response(self.gitea, result)
# adding data not contained in the issue answer # adding data not contained in the issue answer
setattr(issue, "repo", self.name) Issue._add_read_property("repo", self, issue)
setattr(issue, "owner", self.owner) Issue._add_read_property("owner", self.owner, issue)
issues.append(issue) issues.append(issue)
return issues return issues
@ -372,7 +370,7 @@ class Repository(ApiObject):
"title": title, "title": title,
} }
result = self.gitea.requests_post( result = self.gitea.requests_post(
Repository.REPO_ISSUES % (self.owner.username, self.name), data=data Repository.REPO_ISSUES.format(owner=self.owner.username, repo=self.name), data=data
) )
return Issue.parse_response(self.gitea, result) return Issue.parse_response(self.gitea, result)
@ -478,12 +476,12 @@ class Milestone(ApiObject):
def __hash__(self): def __hash__(self):
return hash(self.gitea) ^ hash(self.id) return hash(self.gitea) ^ hash(self.id)
fields_to_parsers = { _fields_to_parsers = {
"closed_at": lambda gitea, t: Util.convert_time(t), "closed_at": lambda gitea, t: Util.convert_time(t),
"due_on": lambda gitea, t: Util.convert_time(t), "due_on": lambda gitea, t: Util.convert_time(t),
} }
patchable_fields = { _patchable_fields = {
"allow_merge_commits", "allow_merge_commits",
"allow_rebase", "allow_rebase",
"allow_rebase_explicit", "allow_rebase_explicit",
@ -517,7 +515,7 @@ class Comment(ApiObject):
def __hash__(self): def __hash__(self):
return hash(self.repo) ^ hash(self.id) return hash(self.repo) ^ hash(self.id)
fields_to_parsers = { _fields_to_parsers = {
"user": lambda gitea, r: User.parse_response(gitea, r), "user": lambda gitea, r: User.parse_response(gitea, r),
"created_at": lambda gitea, t: Util.convert_time(t), "created_at": lambda gitea, t: Util.convert_time(t),
"updated_at": lambda gitea, t: Util.convert_time(t), "updated_at": lambda gitea, t: Util.convert_time(t),
@ -528,7 +526,7 @@ class Commit(ReadonlyApiObject):
def __init__(self, gitea): def __init__(self, gitea):
super(Commit, self).__init__(gitea) super(Commit, self).__init__(gitea)
fields_to_parsers = { _fields_to_parsers = {
# NOTE: api may return None for commiters that are no gitea users # NOTE: api may return None for commiters that are no gitea users
"author": lambda gitea, u: User.parse_response(gitea, u) if u else None "author": lambda gitea, u: User.parse_response(gitea, u) if u else None
} }
@ -551,7 +549,7 @@ class Commit(ReadonlyApiObject):
class Issue(ApiObject): class Issue(ApiObject):
API_OBJECT = """/repos/{owner}/{repo}/issues/{number}""" # <owner, repo, index> API_OBJECT = """/repos/{owner}/{repo}/issues/{index}""" # <owner, repo, index>
GET_TIME = """/repos/%s/%s/issues/%s/times""" # <owner, repo, index> GET_TIME = """/repos/%s/%s/issues/%s/times""" # <owner, repo, index>
GET_COMMENTS = """/repos/%s/%s/issues/comments""" GET_COMMENTS = """/repos/%s/%s/issues/comments"""
CREATE_ISSUE = """/repos/{owner}/{repo}/issues""" CREATE_ISSUE = """/repos/{owner}/{repo}/issues"""
@ -569,15 +567,21 @@ class Issue(ApiObject):
def __hash__(self): def __hash__(self):
return hash(self.repo) ^ hash(self.id) return hash(self.repo) ^ hash(self.id)
fields_to_parsers = { _fields_to_parsers = {
"milestone": lambda gitea, m: Milestone.parse_response(gitea, m), "milestone": lambda gitea, m: Milestone.parse_response(gitea, m),
"user": lambda gitea, u: User.parse_response(gitea, u), "user": lambda gitea, u: User.parse_response(gitea, u),
"assignee": lambda gitea, u: User.parse_response(gitea, u), "assignee": lambda gitea, u: User.parse_response(gitea, u),
"assignees": lambda gitea, us: [User.parse_response(gitea, u) for u in us], "assignees": lambda gitea, us: [User.parse_response(gitea, u) for u in us],
"state": lambda gitea, s: Issue.CLOSED if s == "closed" else Issue.OPENED, "state": lambda gitea, s: Issue.CLOSED if s == "closed" else Issue.OPENED,
# Repository in this request is just a "RepositoryMeta" record, thus request whole object
"repository": lambda gitea, r: Repository.request(gitea, r["owner"], r["name"])
} }
patchable_fields = { _parsers_to_fields = {
"milestone": lambda m: m.id,
}
_patchable_fields = {
"assignee", "assignee",
"assignees", "assignees",
"body", "body",
@ -587,9 +591,15 @@ class Issue(ApiObject):
"title", "title",
} }
def commit(self):
values = self.get_dirty_fields()
args = {"owner": self.repository.owner.username, "repo": self.repository.name, "index": self.number}
self.gitea.requests_patch(Issue.API_OBJECT.format(**args), data=values)
self.dirty_fields = {}
@classmethod @classmethod
def request(cls, gitea, owner, repo, number): def request(cls, gitea, owner, repo, number):
api_object = cls._request(gitea, {"owner": owner, "repo": repo, "number": number}) api_object = cls._request(gitea, {"owner": owner, "repo": repo, "index": number})
return api_object return api_object
@classmethod @classmethod
@ -601,7 +611,7 @@ class Issue(ApiObject):
def get_time_sum(self, user: User) -> int: def get_time_sum(self, user: User) -> int:
results = self.gitea.requests_get( results = self.gitea.requests_get(
Issue.GET_TIME % (self.owner.username, self.repo, self.number) Issue.GET_TIME % (self.owner.username, self.repo.name, self.number)
) )
return sum( return sum(
result["time"] result["time"]
@ -611,22 +621,22 @@ class Issue(ApiObject):
def get_times(self) -> Optional[Dict]: def get_times(self) -> Optional[Dict]:
return self.gitea.requests_get( return self.gitea.requests_get(
Issue.GET_TIME % (self.owner.username, self.repo, self.number) Issue.GET_TIME % (self.owner.username, self.repository.name, self.number)
) )
def delete_time(self, time_id: str): def delete_time(self, time_id: str):
path = f"/repos/{self.owner.username}/{self.repo}/issues/{self.number}/times/{time_id}" path = f"/repos/{self.owner.username}/{self.repository.name}/issues/{self.number}/times/{time_id}"
self.gitea.requests_delete(path) self.gitea.requests_delete(path)
def add_time(self, time: int, created: str = None, user_name: str = None): def add_time(self, time: int, created: str = None, user_name: User = None):
path = f"/repos/{self.owner.username}/{self.repo}/issues/{self.number}/times" path = f"/repos/{self.owner.username}/{self.repository.name}/issues/{self.number}/times"
self.gitea.requests_post( self.gitea.requests_post(
path, data={"created": created, "time": int(time), "user_name": user_name} path, data={"created": created, "time": int(time), "user_name": user_name}
) )
def get_comments(self) -> List[ApiObject]: def get_comments(self) -> List[ApiObject]:
results = self.gitea.requests_get( results = self.gitea.requests_get(
Issue.GET_COMMENTS % (self.owner.username, self.repo) Issue.GET_COMMENTS % (self.owner.username, self.repo.name)
) )
allProjectComments = [ allProjectComments = [
Comment.parse_response(self.gitea, result) for result in results Comment.parse_response(self.gitea, result) for result in results
@ -657,7 +667,7 @@ class Team(ApiObject):
def __hash__(self): def __hash__(self):
return hash(self.organization) ^ hash(self.id) return hash(self.organization) ^ hash(self.id)
fields_to_parsers = { _fields_to_parsers = {
"organization": lambda gitea, o: Organization.parse_response(gitea, o) "organization": lambda gitea, o: Organization.parse_response(gitea, o)
} }
@ -665,7 +675,7 @@ class Team(ApiObject):
def request(cls, gitea, organization, team): def request(cls, gitea, organization, team):
return cls._request(gitea, {"id": id}) return cls._request(gitea, {"id": id})
patchable_fields = {"description", "name", "permission", "units"} _patchable_fields = {"description", "name", "permission", "units"}
def add_user(self, user: User): def add_user(self, user: User):
self.gitea.requests_put(Team.ADD_USER % (self.id, user.login)) self.gitea.requests_put(Team.ADD_USER % (self.id, user.login))

Wyświetl plik

@ -17,7 +17,7 @@ class ReadonlyApiObject:
"""Hash only fields that are part of the gitea-data identity""" """Hash only fields that are part of the gitea-data identity"""
raise MissiongEqualyImplementation() raise MissiongEqualyImplementation()
fields_to_parsers = {} _fields_to_parsers = {}
@classmethod @classmethod
def request(cls, gitea, id): def request(cls, gitea, id):
@ -51,12 +51,12 @@ class ReadonlyApiObject:
@classmethod @classmethod
def _initialize(cls, gitea, api_object, result): def _initialize(cls, gitea, api_object, result):
for name, value in result.items(): for name, value in result.items():
if name in cls.fields_to_parsers and value is not None: if name in cls._fields_to_parsers and value is not None:
parse_func = cls.fields_to_parsers[name] parse_func = cls._fields_to_parsers[name]
value = parse_func(gitea, value) value = parse_func(gitea, value)
cls._add_read_property(name, value, api_object) cls._add_read_property(name, value, api_object)
# add all patchable fields missing in the request to be writable # add all patchable fields missing in the request to be writable
for name in cls.fields_to_parsers.keys(): for name in cls._fields_to_parsers.keys():
if not hasattr(api_object,name): if not hasattr(api_object,name):
cls._add_read_property(name, None, api_object) cls._add_read_property(name, None, api_object)
@ -78,26 +78,35 @@ class ReadonlyApiObject:
class ApiObject(ReadonlyApiObject): class ApiObject(ReadonlyApiObject):
patchable_fields = set() _patchable_fields = set()
def __init__(self, gitea): def __init__(self, gitea):
super().__init__(gitea) super().__init__(gitea)
self.dirty_fields = set() self._dirty_fields = set()
def commit(self): def commit(self):
raise NotImplemented() raise NotImplemented()
_parsers_to_fields = {}
def get_dirty_fields(self): def get_dirty_fields(self):
return {name: getattr(self, name) for name in self.dirty_fields} dirty_fields_values = {}
for field in self._dirty_fields:
value = getattr(self, field)
if field in self._parsers_to_fields:
dirty_fields_values[field] = self._parsers_to_fields[field](value)
else:
dirty_fields_values[field] = value
return dirty_fields_values
@classmethod @classmethod
def _initialize(cls, gitea, api_object, result): def _initialize(cls, gitea, api_object, result):
super()._initialize(gitea,api_object,result) super()._initialize(gitea,api_object,result)
for name, value in result.items(): for name, value in result.items():
if name in cls.patchable_fields: if name in cls._patchable_fields:
cls._add_write_property(name,value,api_object) cls._add_write_property(name,value,api_object)
# add all patchable fields missing in the request to be writable # add all patchable fields missing in the request to be writable
for name in cls.patchable_fields: for name in cls._patchable_fields:
if not hasattr(api_object,name): if not hasattr(api_object,name):
cls._add_write_property(name, None, api_object) cls._add_write_property(name, None, api_object)
@ -112,5 +121,5 @@ class ApiObject(ReadonlyApiObject):
def __set_var(self, name, i): def __set_var(self, name, i):
if self.deleted: if self.deleted:
raise ObjectIsInvalid() raise ObjectIsInvalid()
self.dirty_fields.add(name) self._dirty_fields.add(name)
setattr(self, "_" + name, i) setattr(self, "_" + name, i)

Wyświetl plik

@ -1,7 +1,7 @@
import pytest import pytest
import uuid import uuid
from gitea import Gitea, User, Organization, Team, Repository, Issue from gitea import Gitea, User, Organization, Team, Repository, Issue, Milestone
from gitea import NotFoundException, AlreadyExistsException from gitea import NotFoundException, AlreadyExistsException
# put a ".token" file into your directory containg only the token for gitea # put a ".token" file into your directory containg only the token for gitea
@ -172,6 +172,13 @@ def test_create_team(instance):
assert team.description == "descr" assert team.description == "descr"
assert team.organization == org assert team.organization == org
def test_create_milestone(instance):
org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo)
ms = repo.create_milestone("I love this Milestone", "Find an otter to adopt this milestone")
assert isinstance(ms, Milestone)
assert ms.title == "I love this Milestone"
def test_user_teams(instance): def test_user_teams(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
team = org.get_team(test_team) team = org.get_team(test_team)
@ -206,6 +213,27 @@ def test_hashing(instance):
commit = repo.get_commits()[0] commit = repo.get_commits()[0]
assert len(set([org, team, user, repo, issue, branch, commit, milestone])) assert len(set([org, team, user, repo, issue, branch, commit, milestone]))
def test_change_issue(instance):
org = Organization.request(instance, test_org)
repo = org.get_repositories()[0]
ms_title = "othermilestone"
issue = Issue.create_issue(instance, repo, "IssueTestissue with Testinput", "asdf2332")
new_body = "some new description with some more of that char stuff :)"
issue.body = new_body
issue.commit()
number = issue.number
del issue
issue2 = Issue.request(instance, org.username, repo.name, number)
assert issue2.body == new_body
milestone = repo.create_milestone(ms_title, "this is only a teststone2")
issue2.milestone = milestone
issue2.commit()
del issue2
issues = repo.get_issues()
assert len([issue for issue in issues
if issue.milestone is not None and issue.milestone.title == ms_title]) > 0
def test_team_get_org(instance): def test_team_get_org(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
user = instance.get_user_by_name(test_user) user = instance.get_user_by_name(test_user)

Wyświetl plik

@ -37,3 +37,12 @@ def test_list_repos(instance):
instance.create_repo(org, test_repo + "_" + str(i), str(i)) instance.create_repo(org, test_repo + "_" + str(i), str(i))
repos = org.get_repositories() repos = org.get_repositories()
assert len(repos) >= 33 assert len(repos) >= 33
def test_list_issue(instance):
org = Organization.request(instance, test_org)
repo = Repository.request(instance, org.username, test_repo)
for x in range(0,100):
Issue.create_issue(instance, repo, "TestIssue" + str(x), "We will be to many to be listed")
issues = repo.get_issues()
assert len(issues) > 98