(style) reformatted with python black for easier merges

master
Langenfeld 2023-08-21 10:13:30 +02:00
rodzic 3b8f04e31b
commit 9f6785cdee
11 zmienionych plików z 343 dodań i 223 usunięć

Wyświetl plik

@ -1,10 +1,10 @@
# py-gitea # py-gitea
A very simple API client for Gitea > 1.16.1 A very simple API client for Gitea > 1.16.1
This has been somewhat tested (and used), so most things should work as expected. This has been somewhat tested (and used), so most things should work as expected.
Note that not the full Swagger-API is accessible. The whole implementation is focused Note that not the full Swagger-API is accessible. The whole implementation is focused
on making access and working with Organizations, Teams, Repositories and Users as pain on making access and working with Organizations, Teams, Repositories and Users as pain
free as possible. free as possible.
@ -27,24 +27,25 @@ print("Gitea Version: " + gitea.get_version())
print("API-Token belongs to user: " + gitea.get_user().username) print("API-Token belongs to user: " + gitea.get_user().username)
``` ```
Adding entities like Users, Organizations, ... also is done via the gitea object. Adding entities like Users, Organizations, ... also is done via the gitea object.
```python ```python
user = gitea.create_user("Test Testson", "test@test.test", "password") user = gitea.create_user("Test Testson", "test@test.test", "password")
``` ```
All operations on entities in gitea are then accomplished via the according wrapper objects for those entities. All operations on entities in gitea are then accomplished via the according wrapper objects for those entities.
Each of those objects has a `.request` method that creates an entity according to your gitea instance. Each of those objects has a `.request` method that creates an entity according to your gitea instance.
```python ```python
other_user = User.request(gitea, "OtherUserName") other_user = User.request(gitea, "OtherUserName")
print(other_user.username) print(other_user.username)
``` ```
Note that the fields of the User, Organization,... classes are dynamically created at runtime, and thus not visible during divelopment. Refer to the Gitea-API documentation for the fields names. Note that the fields of the User, Organization,... classes are dynamically created at runtime, and thus not visible
during divelopment. Refer to the Gitea-API documentation for the fields names.
Fields that can not be altered via gitea-api, are read only. After altering a field, the `.commit` method of the
Fields that can not be altered via gitea-api, are read only. After altering a field, the `.commit` method of the according object must be called to synchronize the changed fields with your gitea instance. according object must be called to synchronize the changed fields with your gitea instance.
```python ```python
org = Organization.request(gitea, test_org) org = Organization.request(gitea, test_org)
@ -54,30 +55,31 @@ org.commit()
``` ```
An entity in gitea can be deleted by calling delete. An entity in gitea can be deleted by calling delete.
```python ```python
org.delete() org.delete()
``` ```
All entity objects do have methods to execute some of the requests possible though the gitea-api: All entity objects do have methods to execute some of the requests possible though the gitea-api:
```python ```python
org = Organization.request(gitea, ORGNAME) org = Organization.request(gitea, ORGNAME)
teams = org.get_teams() teams = org.get_teams()
for team in teams: for team in teams:
repos = team.get_repos() repos = team.get_repos()
for repo in repos: for repo in repos:
print(repo.name) print(repo.name)
``` ```
## Installation ## Installation
Use ``pip install py-gitea`` to install. Use ``pip install py-gitea`` to install.
## Tests ## Tests
Tests can be run with: Tests can be run with:
```python3 -m pytest test_api.py``` ```python3 -m pytest test_api.py```
Make sure to have a gitea-instance running on `http://localhost:3000`, and an admin-user token at `.token`. Make sure to have a gitea-instance running on `http://localhost:3000`, and an admin-user token at `.token`.
The admin user must be named ``test``, with email ``secondarytest@test.org``. The admin user must be named ``test``, with email ``secondarytest@test.org``.

Wyświetl plik

@ -9,4 +9,4 @@ from .gitea import (
AlreadyExistsException, AlreadyExistsException,
Issue, Issue,
Milestone, Milestone,
) )

Wyświetl plik

@ -15,21 +15,21 @@ from .apiobject import (
Milestone, Milestone,
Commit, Commit,
Comment, Comment,
Content Content,
) )
__all__ = [ __all__ = [
'Gitea', "Gitea",
'User', "User",
'Organization', "Organization",
'Team', "Team",
'Repository', "Repository",
'Branch', "Branch",
'NotFoundException', "NotFoundException",
'AlreadyExistsException', "AlreadyExistsException",
'Issue', "Issue",
'Milestone', "Milestone",
'Commit', "Commit",
'Comment', "Comment",
'Content' "Content",
] ]

Wyświetl plik

@ -21,18 +21,19 @@ class Organization(ApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Organization): return False if not isinstance(other, Organization):
return False
return self.gitea == other.gitea and self.name == other.name return self.gitea == other.gitea and self.name == other.name
def __hash__(self): def __hash__(self):
return hash(self.gitea) ^ hash(self.name) return hash(self.gitea) ^ hash(self.name)
@classmethod @classmethod
def request(cls, gitea: 'Gitea', name: str) -> 'Organization': def request(cls, gitea: "Gitea", name: str) -> "Organization":
return cls._request(gitea, {"name": name}) return cls._request(gitea, {"name": name})
@classmethod @classmethod
def parse_response(cls, gitea, result) -> 'Organization': 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 for gitea < 1.18 # add "name" field to make this behave similar to users for gitea < 1.18
# also necessary for repository-owner when org is repo owner # also necessary for repository-owner when org is repo owner
@ -40,27 +41,31 @@ class Organization(ApiObject):
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()
args = {"name": self.name} args = {"name": self.name}
self.gitea.requests_patch( self.gitea.requests_patch(Organization.API_OBJECT.format(**args), data=values)
Organization.API_OBJECT.format(**args), data=values
)
self.dirty_fields = {} self.dirty_fields = {}
def create_repo( def create_repo(
self, self,
repoName: str, repoName: str,
description: str = "", description: str = "",
private: bool = False, private: bool = False,
autoInit=True, autoInit=True,
gitignores: str = None, gitignores: str = None,
license: str = None, license: str = None,
readme: str = "Default", readme: str = "Default",
issue_labels: str = None, issue_labels: str = None,
default_branch="master", default_branch="master",
): ):
"""Create an organization Repository """Create an organization Repository
@ -83,7 +88,9 @@ class Organization(ApiObject):
}, },
) )
if "id" in result: if "id" in result:
self.gitea.logger.info("Successfully created Repository %s " % result["name"]) self.gitea.logger.info(
"Successfully created Repository %s " % result["name"]
)
else: else:
self.gitea.logger.error(result["message"]) self.gitea.logger.error(result["message"])
raise Exception("Repository not created... (gitea: %s)" % result["message"]) raise Exception("Repository not created... (gitea: %s)" % result["message"])
@ -108,7 +115,8 @@ class Organization(ApiObject):
) )
teams = [Team.parse_response(self.gitea, result) for result in results] teams = [Team.parse_response(self.gitea, result) for result in results]
# organisation seems to be missing using this request, so we add org manually # organisation seems to be missing using this request, so we add org manually
for t in teams: setattr(t, "_organization", self) for t in teams:
setattr(t, "_organization", self)
return teams return teams
def get_team(self, name) -> "Team": def get_team(self, name) -> "Team":
@ -139,7 +147,7 @@ class Organization(ApiObject):
self.gitea.requests_delete(path) self.gitea.requests_delete(path)
def delete(self): def delete(self):
""" Delete this Organization. Invalidates this Objects data. Also deletes all Repositories owned by the User""" """Delete this Organization. Invalidates this Objects data. Also deletes all Repositories owned by the User"""
for repo in self.get_repositories(): for repo in self.get_repositories():
repo.delete() repo.delete()
self.gitea.requests_delete(Organization.API_OBJECT.format(name=self.username)) self.gitea.requests_delete(Organization.API_OBJECT.format(name=self.username))
@ -167,7 +175,8 @@ class User(ApiObject):
self._emails = [] self._emails = []
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, User): return False if not isinstance(other, User):
return False
return self.gitea == other.gitea and self.id == other.id return self.gitea == other.gitea and self.id == other.id
def __hash__(self): def __hash__(self):
@ -179,7 +188,7 @@ class User(ApiObject):
return self._emails return self._emails
@classmethod @classmethod
def request(cls, gitea: 'Gitea', name: str) -> "User": def request(cls, gitea: "Gitea", name: str) -> "User":
api_object = cls._request(gitea, {"name": name}) api_object = cls._request(gitea, {"name": name})
return api_object return api_object
@ -217,16 +226,16 @@ class User(ApiObject):
self.dirty_fields = {} self.dirty_fields = {}
def create_repo( def create_repo(
self, self,
repoName: str, repoName: str,
description: str = "", description: str = "",
private: bool = False, private: bool = False,
autoInit=True, autoInit=True,
gitignores: str = None, gitignores: str = None,
license: str = None, license: str = None,
readme: str = "Default", readme: str = "Default",
issue_labels: str = None, issue_labels: str = None,
default_branch="master", default_branch="master",
): ):
"""Create a user Repository """Create a user Repository
@ -249,31 +258,33 @@ class User(ApiObject):
}, },
) )
if "id" in result: if "id" in result:
self.gitea.logger.info("Successfully created Repository %s " % result["name"]) self.gitea.logger.info(
"Successfully created Repository %s " % result["name"]
)
else: else:
self.gitea.logger.error(result["message"]) self.gitea.logger.error(result["message"])
raise Exception("Repository not created... (gitea: %s)" % result["message"]) raise Exception("Repository not created... (gitea: %s)" % result["message"])
return Repository.parse_response(self, result) return Repository.parse_response(self, result)
def get_repositories(self) -> List["Repository"]: def get_repositories(self) -> List["Repository"]:
""" Get all Repositories owned by this User.""" """Get all Repositories owned by this User."""
url = f"/users/{self.username}/repos" url = f"/users/{self.username}/repos"
results = self.gitea.requests_get_paginated(url) results = self.gitea.requests_get_paginated(url)
return [Repository.parse_response(self.gitea, result) for result in results] return [Repository.parse_response(self.gitea, result) for result in results]
def get_orgs(self) -> List[Organization]: def get_orgs(self) -> List[Organization]:
""" Get all Organizations this user is a member of.""" """Get all Organizations this user is a member of."""
url = f"/users/{self.username}/orgs" url = f"/users/{self.username}/orgs"
results = self.gitea.requests_get_paginated(url) results = self.gitea.requests_get_paginated(url)
return [Organization.parse_response(self.gitea, result) for result in results] return [Organization.parse_response(self.gitea, result) for result in results]
def get_teams(self) -> List['Team']: def get_teams(self) -> List["Team"]:
url = f"/user/teams" url = f"/user/teams"
results = self.gitea.requests_get_paginated(url, sudo=self) results = self.gitea.requests_get_paginated(url, sudo=self)
return [Team.parse_response(self.gitea, result) for result in results] return [Team.parse_response(self.gitea, result) for result in results]
def get_accessible_repos(self) -> List['Repository']: def get_accessible_repos(self) -> List["Repository"]:
""" Get all Repositories accessible by the logged in User.""" """Get all Repositories accessible by the logged in User."""
results = self.gitea.requests_get("/user/repos", sudo=self) results = self.gitea.requests_get("/user/repos", sudo=self)
return [Repository.parse_response(self, result) for result in results] return [Repository.parse_response(self, result) for result in results]
@ -286,7 +297,7 @@ class User(ApiObject):
self._email = mail["email"] self._email = mail["email"]
def delete(self): def delete(self):
""" Deletes this User. Also deletes all Repositories he owns.""" """Deletes this User. Also deletes all Repositories he owns."""
self.gitea.requests_delete(User.ADMIN_DELETE_USER % self.username) self.gitea.requests_delete(User.ADMIN_DELETE_USER % self.username)
self.deleted = True self.deleted = True
@ -300,7 +311,6 @@ class User(ApiObject):
class Branch(ReadonlyApiObject): class Branch(ReadonlyApiObject):
def __init__(self, gitea): def __init__(self, gitea):
super().__init__(gitea) super().__init__(gitea)
@ -318,13 +328,15 @@ class Branch(ReadonlyApiObject):
} }
@classmethod @classmethod
def request(cls, gitea: 'Gitea', owner: str, repo: str, ref: str): def request(cls, gitea: "Gitea", owner: str, repo: str, ref: str):
return cls._request(gitea, {"owner": owner, "repo": repo, "ref": ref}) return cls._request(gitea, {"owner": owner, "repo": repo, "ref": ref})
class Repository(ApiObject): class Repository(ApiObject):
API_OBJECT = """/repos/{owner}/{name}""" # <owner>, <reponame> API_OBJECT = """/repos/{owner}/{name}""" # <owner>, <reponame>
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/{owner}/{repo}/issues""" # <owner, reponame> REPO_ISSUES = """/repos/{owner}/{repo}/issues""" # <owner, reponame>
@ -339,7 +351,8 @@ class Repository(ApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Repository): return False if not isinstance(other, Repository):
return False
return self.owner == other.owner and self.name == other.name return self.owner == other.owner and self.name == other.name
def __hash__(self): def __hash__(self):
@ -348,12 +361,13 @@ class Repository(ApiObject):
_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"] == "" else User.parse_response(gitea, r), if r["email"] == ""
else User.parse_response(gitea, r),
"updated_at": lambda gitea, t: Util.convert_time(t), "updated_at": lambda gitea, t: Util.convert_time(t),
} }
@classmethod @classmethod
def request(cls, gitea: 'Gitea', owner: str, name: str): def request(cls, gitea: "Gitea", owner: str, name: str):
return cls._request(gitea, {"owner": owner, "name": name}) return cls._request(gitea, {"owner": owner, "name": name})
_patchable_fields = { _patchable_fields = {
@ -391,7 +405,7 @@ class Repository(ApiObject):
self.gitea.requests_patch(self.API_OBJECT.format(**args), data=values) self.gitea.requests_patch(self.API_OBJECT.format(**args), data=values)
self.dirty_fields = {} self.dirty_fields = {}
def get_branches(self) -> List['Branch']: def get_branches(self) -> List["Branch"]:
"""Get all the Branches of this Repository.""" """Get all the Branches of this Repository."""
results = self.gitea.requests_get( results = self.gitea.requests_get(
Repository.REPO_BRANCHES % (self.owner.username, self.name) Repository.REPO_BRANCHES % (self.owner.username, self.name)
@ -431,7 +445,8 @@ class Repository(ApiObject):
issues = [] issues = []
data = {"state": state} data = {"state": state}
results = self.gitea.requests_get_paginated( results = self.gitea.requests_get_paginated(
Repository.REPO_ISSUES.format(owner=self.owner.username, repo=self.name), params=data Repository.REPO_ISSUES.format(owner=self.owner.username, repo=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)
@ -467,14 +482,20 @@ class Repository(ApiObject):
"title": title, "title": title,
} }
result = self.gitea.requests_post( result = self.gitea.requests_post(
Repository.REPO_ISSUES.format(owner=self.owner.username, repo=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)
def create_milestone(self, title: str, description: str, due_date: str = None, state: str = "open") -> "Milestone": def create_milestone(
url = Repository.REPO_MILESTONES.format(owner=self.owner.username, repo=self.name) self, title: str, description: str, due_date: str = None, state: str = "open"
) -> "Milestone":
url = Repository.REPO_MILESTONES.format(
owner=self.owner.username, repo=self.name
)
data = {"title": title, "description": description, "state": state} data = {"title": title, "description": description, "state": state}
if due_date: data["due_date"] = due_date if due_date:
data["due_date"] = due_date
result = self.gitea.requests_post(url, data=data) result = self.gitea.requests_post(url, data=data)
return Milestone.parse_response(self.gitea, result) return Milestone.parse_response(self.gitea, result)
@ -528,11 +549,17 @@ class Repository(ApiObject):
url = f"/repos/{self.owner.username}/{self.name}/collaborators/{user_name}" url = f"/repos/{self.owner.username}/{self.name}/collaborators/{user_name}"
self.gitea.requests_delete(url) self.gitea.requests_delete(url)
def transfer_ownership(self, new_owner: Union["User", "Organization"], new_teams: Set["Team"] = frozenset()): def transfer_ownership(
self,
new_owner: Union["User", "Organization"],
new_teams: Set["Team"] = frozenset(),
):
url = Repository.REPO_TRANSFER.format(owner=self.owner.username, repo=self.name) url = Repository.REPO_TRANSFER.format(owner=self.owner.username, repo=self.name)
data = {"new_owner": new_owner.username} data = {"new_owner": new_owner.username}
if isinstance(new_owner, Organization): if isinstance(new_owner, Organization):
new_team_ids = [team.id for team in new_teams if team in new_owner.get_teams()] new_team_ids = [
team.id for team in new_teams if team in new_owner.get_teams()
]
data["team_ids"] = new_team_ids data["team_ids"] = new_team_ids
self.gitea.requests_post(url, data=data) self.gitea.requests_post(url, data=data)
# TODO: make sure this instance is either updated or discarded # TODO: make sure this instance is either updated or discarded
@ -541,17 +568,25 @@ class Repository(ApiObject):
"""https://try.gitea.io/api/swagger#/repository/repoGetContentsList""" """https://try.gitea.io/api/swagger#/repository/repoGetContentsList"""
url = f"/repos/{self.owner.username}/{self.name}/contents" url = f"/repos/{self.owner.username}/{self.name}/contents"
data = {"ref": commit.sha} if commit else {} data = {"ref": commit.sha} if commit else {}
result = [Content.parse_response(self.gitea, f) for f in self.gitea.requests_get(url, data)] result = [
Content.parse_response(self.gitea, f)
for f in self.gitea.requests_get(url, data)
]
return result return result
def get_file_content(self, content: "Content", commit: "Commit" = None) -> Union[str, List["Content"]]: def get_file_content(
self, content: "Content", commit: "Commit" = None
) -> Union[str, List["Content"]]:
"""https://try.gitea.io/api/swagger#/repository/repoGetContents""" """https://try.gitea.io/api/swagger#/repository/repoGetContents"""
url = f"/repos/{self.owner.username}/{self.name}/contents/{content.path}" url = f"/repos/{self.owner.username}/{self.name}/contents/{content.path}"
data = {"ref": commit.sha} if commit else {} data = {"ref": commit.sha} if commit else {}
if content.type == Content.FILE: if content.type == Content.FILE:
return self.gitea.requests_get(url, data)["content"] return self.gitea.requests_get(url, data)["content"]
else: else:
return [Content.parse_response(self.gitea, f) for f in self.gitea.requests_get(url, data)] return [
Content.parse_response(self.gitea, f)
for f in self.gitea.requests_get(url, data)
]
def create_file(self, file_path: str, content: str, data: dict = None): def create_file(self, file_path: str, content: str, data: dict = None):
"""https://try.gitea.io/api/swagger#/repository/repoCreateFile""" """https://try.gitea.io/api/swagger#/repository/repoCreateFile"""
@ -561,7 +596,9 @@ class Repository(ApiObject):
data.update({"content": content}) data.update({"content": content})
return self.gitea.requests_post(url, data) return self.gitea.requests_post(url, data)
def change_file(self, file_path: str, file_sha: str, content: str, data: dict = None): def change_file(
self, file_path: str, file_sha: str, content: str, data: dict = None
):
"""https://try.gitea.io/api/swagger#/repository/repoCreateFile""" """https://try.gitea.io/api/swagger#/repository/repoCreateFile"""
if not data: if not data:
data = {} data = {}
@ -583,7 +620,8 @@ class Milestone(ApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Milestone): return False if not isinstance(other, Milestone):
return False
return self.gitea == other.gitea and self.id == other.id return self.gitea == other.gitea and self.id == other.id
def __hash__(self): def __hash__(self):
@ -612,17 +650,17 @@ class Milestone(ApiObject):
} }
@classmethod @classmethod
def request(cls, gitea: 'Gitea', owner: str, repo: str, number: str): def request(cls, gitea: "Gitea", owner: str, repo: str, number: str):
return cls._request(gitea, {"owner": owner, "repo": repo, "number": number}) return cls._request(gitea, {"owner": owner, "repo": repo, "number": number})
class Comment(ApiObject): class Comment(ApiObject):
def __init__(self, gitea): def __init__(self, gitea):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Comment): return False if not isinstance(other, Comment):
return False
return self.repo == other.repo and self.id == other.id return self.repo == other.repo and self.id == other.id
def __hash__(self): def __hash__(self):
@ -636,24 +674,26 @@ class Comment(ApiObject):
class Commit(ReadonlyApiObject): class Commit(ReadonlyApiObject):
def __init__(self, gitea): def __init__(self, gitea):
super().__init__(gitea) super().__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
} }
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Commit): return False if not isinstance(other, Commit):
return False
return self.sha == other.sha return self.sha == other.sha
def __hash__(self): def __hash__(self):
return hash(self.sha) return hash(self.sha)
@classmethod @classmethod
def parse_response(cls, gitea, result) -> 'Commit': def parse_response(cls, gitea, result) -> "Commit":
commit_cache = result["commit"] commit_cache = result["commit"]
api_object = cls(gitea) api_object = cls(gitea)
cls._initialize(gitea, api_object, result) cls._initialize(gitea, api_object, result)
@ -675,7 +715,8 @@ class Issue(ApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Issue): return False if not isinstance(other, Issue):
return False
return self.repo == other.repo and self.id == other.id return self.repo == other.repo and self.id == other.id
def __hash__(self): def __hash__(self):
@ -688,7 +729,7 @@ class Issue(ApiObject):
"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 in this request is just a "RepositoryMeta" record, thus request whole object
"repository": lambda gitea, r: Repository.request(gitea, r["owner"], r["name"]) "repository": lambda gitea, r: Repository.request(gitea, r["owner"], r["name"]),
} }
_parsers_to_fields = { _parsers_to_fields = {
@ -707,13 +748,19 @@ class Issue(ApiObject):
def commit(self): def commit(self):
values = self.get_dirty_fields() values = self.get_dirty_fields()
args = {"owner": self.repository.owner.username, "repo": self.repository.name, "index": self.number} 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.gitea.requests_patch(Issue.API_OBJECT.format(**args), data=values)
self.dirty_fields = {} self.dirty_fields = {}
@classmethod @classmethod
def request(cls, gitea: 'Gitea', owner: str, repo: str, number: str): def request(cls, gitea: "Gitea", owner: str, repo: str, number: str):
api_object = cls._request(gitea, {"owner": owner, "repo": repo, "index": number}) api_object = cls._request(
gitea, {"owner": owner, "repo": repo, "index": number}
)
return api_object return api_object
@classmethod @classmethod
@ -774,7 +821,8 @@ class Team(ApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Team): return False if not isinstance(other, Team):
return False
return self.organization == other.organization and self.id == other.id return self.organization == other.organization and self.id == other.id
def __hash__(self): def __hash__(self):
@ -813,12 +861,12 @@ class Team(ApiObject):
self.gitea.requests_put(Team.ADD_REPO % (self.id, org, repo.name)) self.gitea.requests_put(Team.ADD_REPO % (self.id, org, repo.name))
def get_members(self): def get_members(self):
""" Get all users assigned to the team. """ """Get all users assigned to the team."""
results = self.gitea.requests_get(Team.GET_MEMBERS % self.id) results = self.gitea.requests_get(Team.GET_MEMBERS % self.id)
return [User.parse_response(self.gitea, result) for result in results] return [User.parse_response(self.gitea, result) for result in results]
def get_repos(self): def get_repos(self):
""" Get all repos of this Team.""" """Get all repos of this Team."""
results = self.gitea.requests_get(Team.GET_REPOS % self.id) results = self.gitea.requests_get(Team.GET_REPOS % self.id)
return [Repository.parse_response(self.gitea, result) for result in results] return [Repository.parse_response(self.gitea, result) for result in results]
@ -838,8 +886,11 @@ class Content(ReadonlyApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Team): return False if not isinstance(other, Team):
return self.repo == self.repo and self.sha == other.sha and self.name == other.name return False
return (
self.repo == self.repo and self.sha == other.sha and self.name == other.name
)
def __hash__(self): def __hash__(self):
return hash(self.repo) ^ hash(self.sha) ^ hash(self.name) return hash(self.repo) ^ hash(self.sha) ^ hash(self.name)
@ -848,7 +899,7 @@ class Content(ReadonlyApiObject):
class Util: class Util:
@staticmethod @staticmethod
def convert_time(time: str) -> datetime: def convert_time(time: str) -> datetime:
""" Parsing of strange Gitea time format ("%Y-%m-%dT%H:%M:%S:%z" but with ":" in time zone notation)""" """Parsing of strange Gitea time format ("%Y-%m-%dT%H:%M:%S:%z" but with ":" in time zone notation)"""
try: try:
return datetime.strptime(time[:-3] + "00", "%Y-%m-%dT%H:%M:%S%z") return datetime.strptime(time[:-3] + "00", "%Y-%m-%dT%H:%M:%S%z")
except ValueError: except ValueError:

Wyświetl plik

@ -1,8 +1,11 @@
from .exceptions import ObjectIsInvalid, MissiongEqualyImplementation, RawRequestEndpointMissing from .exceptions import (
ObjectIsInvalid,
MissiongEqualyImplementation,
RawRequestEndpointMissing,
)
class ReadonlyApiObject: class ReadonlyApiObject:
def __init__(self, gitea): def __init__(self, gitea):
self.gitea = gitea self.gitea = gitea
self.deleted = False # set if .delete was called, so that an exception is risen self.deleted = False # set if .delete was called, so that an exception is risen
@ -35,7 +38,7 @@ class ReadonlyApiObject:
@classmethod @classmethod
def _get_gitea_api_object(cls, gitea, args): def _get_gitea_api_object(cls, gitea, args):
"""Retrieving an object always as GET_API_OBJECT """ """Retrieving an object always as GET_API_OBJECT"""
return gitea.requests_get(cls.API_OBJECT.format(**args)) return gitea.requests_get(cls.API_OBJECT.format(**args))
@classmethod @classmethod
@ -61,8 +64,7 @@ class ReadonlyApiObject:
def _add_read_property(cls, name, value, api_object): def _add_read_property(cls, name, value, api_object):
if not hasattr(api_object, name): if not hasattr(api_object, name):
setattr(api_object, "_" + name, value) setattr(api_object, "_" + name, value)
prop = property( prop = property((lambda n: lambda self: self._get_var(n))(name))
(lambda n: lambda self: self._get_var(n))(name))
setattr(cls, name, prop) setattr(cls, name, prop)
else: else:
raise AttributeError(f"Attribute {name} already exists on api object.") raise AttributeError(f"Attribute {name} already exists on api object.")
@ -107,7 +109,8 @@ class ApiObject(ReadonlyApiObject):
setattr(api_object, "_" + name, value) setattr(api_object, "_" + name, value)
prop = property( prop = property(
(lambda n: lambda self: self._get_var(n))(name), (lambda n: lambda self: self._get_var(n))(name),
(lambda n: lambda self, v: self.__set_var(n, v))(name)) (lambda n: lambda self, v: self.__set_var(n, v))(name),
)
setattr(cls, name, prop) setattr(cls, name, prop)
def __set_var(self, name, i): def __set_var(self, name, i):

Wyświetl plik

@ -17,6 +17,7 @@ class ConflictException(Exception):
class RawRequestEndpointMissing(Exception): class RawRequestEndpointMissing(Exception):
"""This ApiObject can only be obtained through other api objects and does not have """This ApiObject can only be obtained through other api objects and does not have
diret .request method.""" diret .request method."""
pass pass
@ -25,4 +26,5 @@ class MissiongEqualyImplementation(Exception):
Each Object obtained from the gitea api must be able to check itself for equality in relation to its Each Object obtained from the gitea api must be able to check itself for equality in relation to its
fields obtained from gitea. Risen if an api object is lacking the proper implementation. fields obtained from gitea. Risen if an api object is lacking the proper implementation.
""" """
pass pass

Wyświetl plik

@ -11,7 +11,8 @@ from .exceptions import NotFoundException, ConflictException, AlreadyExistsExcep
class Gitea: class Gitea:
""" Object to establish a session with Gitea. """ """Object to establish a session with Gitea."""
ADMIN_CREATE_USER = """/admin/users""" ADMIN_CREATE_USER = """/admin/users"""
GET_USERS_ADMIN = """/admin/users""" GET_USERS_ADMIN = """/admin/users"""
ADMIN_REPO_CREATE = """/admin/users/%s/repos""" # <ownername> ADMIN_REPO_CREATE = """/admin/users/%s/repos""" # <ownername>
@ -21,14 +22,9 @@ class Gitea:
CREATE_TEAM = """/orgs/%s/teams""" # <orgname> CREATE_TEAM = """/orgs/%s/teams""" # <orgname>
def __init__( def __init__(
self, self, gitea_url: str, token_text=None, auth=None, verify=True, log_level="INFO"
gitea_url: str, ):
token_text=None, """Initializing Gitea-instance
auth=None,
verify=True,
log_level="INFO"
):
""" Initializing Gitea-instance
Args: Args:
gitea_url (str): The Gitea instance URL. gitea_url (str): The Gitea instance URL.
@ -60,7 +56,6 @@ class Gitea:
if not verify: if not verify:
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def __get_url(self, endpoint): def __get_url(self, endpoint):
url = self.url + "/api/v1" + endpoint url = self.url + "/api/v1" + endpoint
self.logger.debug("Url: %s" % url) self.logger.debug("Url: %s" % url)
@ -68,7 +63,7 @@ class Gitea:
@staticmethod @staticmethod
def parse_result(result) -> Dict: def parse_result(result) -> Dict:
""" Parses the result-JSON to a dict. """ """Parses the result-JSON to a dict."""
if result.text and len(result.text) > 3: if result.text and len(result.text) > 3:
return json.loads(result.text) return json.loads(result.text)
return {} return {}
@ -78,19 +73,25 @@ class Gitea:
combined_params.update(params) combined_params.update(params)
if sudo: if sudo:
combined_params["sudo"] = sudo.username combined_params["sudo"] = sudo.username
request = self.requests.get(self.__get_url(endpoint), headers=self.headers, params=combined_params) request = self.requests.get(
self.__get_url(endpoint), headers=self.headers, params=combined_params
)
if request.status_code not in [200, 201]: if request.status_code not in [200, 201]:
message = f"Received status code: {request.status_code} ({request.url})" message = f"Received status code: {request.status_code} ({request.url})"
if request.status_code in [404]: if request.status_code in [404]:
raise NotFoundException(message) raise NotFoundException(message)
if request.status_code in [403]: if request.status_code in [403]:
raise Exception(f"Unauthorized: {request.url} - Check your permissions and try again! ({message})") raise Exception(
f"Unauthorized: {request.url} - Check your permissions and try again! ({message})"
)
if request.status_code in [409]: if request.status_code in [409]:
raise ConflictException(message) raise ConflictException(message)
raise Exception(message) raise Exception(message)
return self.parse_result(request) return self.parse_result(request)
def requests_get_paginated(self, endpoint: str, params=frozendict(), sudo=None, page_key: str = "page"): def requests_get_paginated(
self, endpoint: str, params=frozendict(), sudo=None, page_key: str = "page"
):
page = 1 page = 1
combined_params = {} combined_params = {}
combined_params.update(params) combined_params.update(params)
@ -106,7 +107,9 @@ class Gitea:
def requests_put(self, endpoint: str, data: dict = None): def requests_put(self, endpoint: str, data: dict = None):
if not data: if not data:
data = {} data = {}
request = self.requests.put(self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)) request = self.requests.put(
self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)
)
if request.status_code not in [200, 204]: if request.status_code not in [200, 204]:
message = f"Received status code: {request.status_code} ({request.url}) {request.text}" message = f"Received status code: {request.status_code} ({request.url}) {request.text}"
self.logger.error(message) self.logger.error(message)
@ -120,21 +123,34 @@ class Gitea:
raise Exception(message) raise Exception(message)
def requests_post(self, endpoint: str, data: dict): def requests_post(self, endpoint: str, data: dict):
request = self.requests.post(self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)) request = self.requests.post(
self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)
)
if request.status_code not in [200, 201, 202]: if request.status_code not in [200, 201, 202]:
if ("already exists" in request.text or "e-mail already in use" in request.text): if (
"already exists" in request.text
or "e-mail already in use" in request.text
):
self.logger.warning(request.text) self.logger.warning(request.text)
raise AlreadyExistsException() raise AlreadyExistsException()
self.logger.error(f"Received status code: {request.status_code} ({request.url})") self.logger.error(
f"Received status code: {request.status_code} ({request.url})"
)
self.logger.error(f"With info: {data} ({self.headers})") self.logger.error(f"With info: {data} ({self.headers})")
self.logger.error(f"Answer: {request.text}") self.logger.error(f"Answer: {request.text}")
raise Exception(f"Received status code: {request.status_code} ({request.url}), {request.text}") raise Exception(
f"Received status code: {request.status_code} ({request.url}), {request.text}"
)
return self.parse_result(request) return self.parse_result(request)
def requests_patch(self, endpoint: str, data: dict): def requests_patch(self, endpoint: str, data: dict):
request = self.requests.patch(self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)) request = self.requests.patch(
self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)
)
if request.status_code not in [200, 201]: if request.status_code not in [200, 201]:
error_message = f"Received status code: {request.status_code} ({request.url}) {data}" error_message = (
f"Received status code: {request.status_code} ({request.url}) {data}"
)
self.logger.error(error_message) self.logger.error(error_message)
raise Exception(error_message) raise Exception(error_message)
return self.parse_result(request) return self.parse_result(request)
@ -175,17 +191,17 @@ class Gitea:
return None return None
def create_user( def create_user(
self, self,
user_name: str, user_name: str,
email: str, email: str,
password: str, password: str,
full_name: str = None, full_name: str = None,
login_name: str = None, login_name: str = None,
change_pw=True, change_pw=True,
send_notify=True, send_notify=True,
source_id=0, source_id=0,
): ):
""" Create User. """Create User.
Throws: Throws:
AlreadyExistsException, if the User exists already AlreadyExistsException, if the User exists already
Exception, if something else went wrong. Exception, if something else went wrong.
@ -222,19 +238,19 @@ class Gitea:
return user return user
def create_repo( def create_repo(
self, self,
repoOwner: Union[User, Organization], repoOwner: Union[User, Organization],
repoName: str, repoName: str,
description: str = "", description: str = "",
private: bool = False, private: bool = False,
autoInit=True, autoInit=True,
gitignores: str = None, gitignores: str = None,
license: str = None, license: str = None,
readme: str = "Default", readme: str = "Default",
issue_labels: str = None, issue_labels: str = None,
default_branch="master", default_branch="master",
): ):
""" Create a Repository as the administrator """Create a Repository as the administrator
Throws: Throws:
AlreadyExistsException: If the Repository exists already. AlreadyExistsException: If the Repository exists already.
@ -269,13 +285,13 @@ class Gitea:
return Repository.parse_response(self, result) return Repository.parse_response(self, result)
def create_org( def create_org(
self, self,
owner: User, owner: User,
orgName: str, orgName: str,
description: str, description: str,
location="", location="",
website="", website="",
full_name="", full_name="",
): ):
assert isinstance(owner, User) assert isinstance(owner, User)
result = self.requests_post( result = self.requests_post(
@ -303,24 +319,24 @@ class Gitea:
return Organization.parse_response(self, result) return Organization.parse_response(self, result)
def create_team( def create_team(
self, self,
org: Organization, org: Organization,
name: str, name: str,
description: str = "", description: str = "",
permission: str = "read", permission: str = "read",
can_create_org_repo: bool = False, can_create_org_repo: bool = False,
includes_all_repositories: bool = False, includes_all_repositories: bool = False,
units=( units=(
"repo.code", "repo.code",
"repo.issues", "repo.issues",
"repo.ext_issues", "repo.ext_issues",
"repo.wiki", "repo.wiki",
"repo.pulls", "repo.pulls",
"repo.releases", "repo.releases",
"repo.ext_wiki", "repo.ext_wiki",
), ),
): ):
""" Creates a Team. """Creates a Team.
Args: Args:
org (Organization): Organization the Team will be part of. org (Organization): Organization the Team will be part of.

Wyświetl plik

@ -1,35 +1,31 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
with open('README.md') as readme_file: with open("README.md") as readme_file:
README = readme_file.read() README = readme_file.read()
setup_args = dict( setup_args = dict(
name='py-gitea', name="py-gitea",
version='0.2.6', version="0.2.6",
description='A python wrapper for the Gitea API', description="A python wrapper for the Gitea API",
long_description_content_type="text/markdown", long_description_content_type="text/markdown",
long_description=README, long_description=README,
license='MIT', license="MIT",
packages=find_packages(), packages=find_packages(),
author='Vincent Langenfeld ', author="Vincent Langenfeld ",
author_email='langenfv@tf.uni-freiburg.de', author_email="langenfv@tf.uni-freiburg.de",
keywords=['Gitea','api','wrapper'], keywords=["Gitea", "api", "wrapper"],
url='https://github.com/Langenfeld/py-gitea', url="https://github.com/Langenfeld/py-gitea",
download_url='https://pypi.org/project/py-gitea/' download_url="https://pypi.org/project/py-gitea/",
) )
install_requires = [ install_requires = [
'requests', "requests",
'frozendict', "frozendict",
] ]
extras_require = { extras_require = {"test": ["pytest"]}
'test': ['pytest']
}
if __name__ == '__main__': if __name__ == "__main__":
setup( setup(
**setup_args, **setup_args, install_requires=install_requires, extras_require=extras_require
install_requires=install_requires,
extras_require=extras_require
) )

Wyświetl plik

@ -13,6 +13,7 @@ import pytest
from gitea import Gitea from gitea import Gitea
@pytest.fixture @pytest.fixture
def instance(scope="module"): def instance(scope="module"):
try: try:

Wyświetl plik

@ -6,6 +6,7 @@ import uuid
from gitea import Gitea, User, Organization, Team, Repository, Issue, Milestone 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
@pytest.fixture @pytest.fixture
def instance(scope="module"): def instance(scope="module"):
@ -22,10 +23,13 @@ def instance(scope="module"):
- Token at .token \ - Token at .token \
?" ?"
# make up some fresh names for the tests run # make up some fresh names for the tests run
test_org = "org_" + uuid.uuid4().hex[:8] test_org = "org_" + uuid.uuid4().hex[:8]
test_user = "user_" + uuid.uuid4().hex[:8] test_user = "user_" + uuid.uuid4().hex[:8]
test_team = "team_" + uuid.uuid4().hex[:8] # team names seem to have a rather low max lenght test_team = (
"team_" + uuid.uuid4().hex[:8]
) # team names seem to have a rather low max lenght
test_repo = "repo_" + uuid.uuid4().hex[:8] test_repo = "repo_" + uuid.uuid4().hex[:8]
@ -65,6 +69,7 @@ def test_create_user(instance):
assert type(user.id) is int assert type(user.id) is int
assert user.is_admin is False assert user.is_admin is False
def test_change_user(instance): def test_change_user(instance):
user = instance.get_user_by_name(test_user) user = instance.get_user_by_name(test_user)
location = "a house" location = "a house"
@ -72,7 +77,7 @@ def test_change_user(instance):
new_fullname = "Other Test Full Name" new_fullname = "Other Test Full Name"
user.full_name = new_fullname user.full_name = new_fullname
user.commit(user.username, 0) user.commit(user.username, 0)
del(user) del user
user = instance.get_user_by_name(test_user) user = instance.get_user_by_name(test_user)
assert user.full_name == new_fullname assert user.full_name == new_fullname
assert user.location == location assert user.location == location
@ -153,6 +158,7 @@ def test_list_branches(instance):
master = [b for b in branches if b.name == "master"] master = [b for b in branches if b.name == "master"]
assert len(master) > 0 assert len(master) > 0
def test_list_files_and_content(instance): def test_list_files_and_content(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo) repo = org.get_repository(test_repo)
@ -165,13 +171,13 @@ def test_list_files_and_content(instance):
assert len(readme_content) > 0 assert len(readme_content) > 0
assert "descr" in str(base64.b64decode(readme_content)) assert "descr" in str(base64.b64decode(readme_content))
def test_create_file(instance): def test_create_file(instance):
TESTFILE_CONENTE = "TestStringFileContent" TESTFILE_CONENTE = "TestStringFileContent"
TESTFILE_CONENTE_B64 = base64.b64encode(bytes(TESTFILE_CONENTE, 'utf-8')) TESTFILE_CONENTE_B64 = base64.b64encode(bytes(TESTFILE_CONENTE, "utf-8"))
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo) repo = org.get_repository(test_repo)
repo.create_file("testfile.md", repo.create_file("testfile.md", content=TESTFILE_CONENTE_B64.decode("ascii"))
content = TESTFILE_CONENTE_B64.decode("ascii"))
# test if putting was successful # test if putting was successful
content = repo.get_git_content() content = repo.get_git_content()
readmes = [c for c in content if c.name == "testfile.md"] readmes = [c for c in content if c.name == "testfile.md"]
@ -180,17 +186,19 @@ def test_create_file(instance):
assert len(readme_content) > 0 assert len(readme_content) > 0
assert TESTFILE_CONENTE in str(base64.b64decode(readme_content)) assert TESTFILE_CONENTE in str(base64.b64decode(readme_content))
def test_change_file(instance): def test_change_file(instance):
TESTFILE_CONENTE = "TestStringFileContent with changed content now" TESTFILE_CONENTE = "TestStringFileContent with changed content now"
TESTFILE_CONENTE_B64 = base64.b64encode(bytes(TESTFILE_CONENTE, 'utf-8')) TESTFILE_CONENTE_B64 = base64.b64encode(bytes(TESTFILE_CONENTE, "utf-8"))
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo) repo = org.get_repository(test_repo)
#figure out the sha of the file to change # figure out the sha of the file to change
content = repo.get_git_content() content = repo.get_git_content()
readmes = [c for c in content if c.name == "testfile.md"] readmes = [c for c in content if c.name == "testfile.md"]
# change # change
repo.change_file("testfile.md", readmes[0].sha, repo.change_file(
content = TESTFILE_CONENTE_B64.decode("ascii")) "testfile.md", readmes[0].sha, content=TESTFILE_CONENTE_B64.decode("ascii")
)
# test if putting was successful # test if putting was successful
content = repo.get_git_content() content = repo.get_git_content()
readmes = [c for c in content if c.name == "testfile.md"] readmes = [c for c in content if c.name == "testfile.md"]
@ -199,6 +207,7 @@ def test_change_file(instance):
assert len(readme_content) > 0 assert len(readme_content) > 0
assert TESTFILE_CONENTE in str(base64.b64decode(readme_content)) assert TESTFILE_CONENTE in str(base64.b64decode(readme_content))
def test_create_branch(instance): def test_create_branch(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo) repo = org.get_repository(test_repo)
@ -207,6 +216,7 @@ def test_create_branch(instance):
assert len(master) > 0 assert len(master) > 0
repo.add_branch(master[0], "test_branch") repo.add_branch(master[0], "test_branch")
def test_create_team(instance): def test_create_team(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
team = instance.create_team(org, test_team, "descr") team = instance.create_team(org, test_team, "descr")
@ -214,6 +224,7 @@ def test_create_team(instance):
assert team.description == "descr" assert team.description == "descr"
assert team.organization == org assert team.organization == org
def test_patch_team(instance): def test_patch_team(instance):
fields = { fields = {
"can_create_org_repo": True, "can_create_org_repo": True,
@ -238,12 +249,16 @@ def test_request_team(instance):
team2 = Team.request(instance, team.id) team2 = Team.request(instance, team.id)
assert team.name == team2.name assert team.name == team2.name
def test_create_milestone(instance): def test_create_milestone(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo) repo = org.get_repository(test_repo)
ms = repo.create_milestone("I love this Milestone", "Find an otter to adopt this milestone") ms = repo.create_milestone(
assert isinstance(ms, Milestone) "I love this Milestone", "Find an otter to adopt this milestone"
assert ms.title == "I love 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)
@ -253,11 +268,13 @@ def test_user_teams(instance):
teams = user.get_teams() teams = user.get_teams()
assert team in teams assert team in teams
def test_get_accessible_repositories(instance): def test_get_accessible_repositories(instance):
user = instance.get_user_by_name(test_user) user = instance.get_user_by_name(test_user)
repos = user.get_accessible_repos() repos = user.get_accessible_repos()
assert len(repos) > 0 assert len(repos) > 0
def test_create_issue(instance): def test_create_issue(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = Repository.request(instance, org.username, test_repo) repo = Repository.request(instance, org.username, test_repo)
@ -266,12 +283,13 @@ def test_create_issue(instance):
assert issue.title == "TestIssue" assert issue.title == "TestIssue"
assert issue.body == "Body text with this issue" assert issue.body == "Body text with this issue"
def test_hashing(instance): def test_hashing(instance):
#just call the hash function of each object to see if something bad happens # just call the hash function of each object to see if something bad happens
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
team = org.get_team(test_team) team = org.get_team(test_team)
user = instance.get_user_by_name(test_user) user = instance.get_user_by_name(test_user)
#TODO test for milestones (Todo: add milestone adding) # TODO test for milestones (Todo: add milestone adding)
repo = org.get_repositories()[0] repo = org.get_repositories()[0]
milestone = repo.create_milestone("mystone", "this is only a teststone") milestone = repo.create_milestone("mystone", "this is only a teststone")
issue = repo.get_issues()[0] issue = repo.get_issues()[0]
@ -284,7 +302,9 @@ def test_change_issue(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repositories()[0] repo = org.get_repositories()[0]
ms_title = "othermilestone" ms_title = "othermilestone"
issue = Issue.create_issue(instance, repo, "IssueTestissue with Testinput", "asdf2332") issue = Issue.create_issue(
instance, repo, "IssueTestissue with Testinput", "asdf2332"
)
new_body = "some new description with some more of that char stuff :)" new_body = "some new description with some more of that char stuff :)"
issue.body = new_body issue.body = new_body
issue.commit() issue.commit()
@ -300,8 +320,17 @@ def test_change_issue(instance):
assert issue3.milestone is not None assert issue3.milestone is not None
assert issue3.milestone.description == "this is only a teststone2" assert issue3.milestone.description == "this is only a teststone2"
issues = repo.get_issues() issues = repo.get_issues()
assert len([issue for issue in issues assert (
if issue.milestone is not None and issue.milestone.title == ms_title]) > 0 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)
@ -309,6 +338,7 @@ def test_team_get_org(instance):
teams = user.get_teams() teams = user.get_teams()
assert org.username == teams[0].organization.name assert org.username == teams[0].organization.name
def test_delete_repo_userowned(instance): def test_delete_repo_userowned(instance):
user = User.request(instance, test_user) user = User.request(instance, test_user)
repo = Repository.request(instance, user.username, test_repo) repo = Repository.request(instance, user.username, test_repo)
@ -316,6 +346,7 @@ def test_delete_repo_userowned(instance):
with pytest.raises(NotFoundException) as e: with pytest.raises(NotFoundException) as e:
Repository.request(instance, test_user, test_repo) Repository.request(instance, test_user, test_repo)
def test_secundary_email(instance): def test_secundary_email(instance):
SECONDARYMAIL = "secondarytest@test.org" # set up with real email SECONDARYMAIL = "secondarytest@test.org" # set up with real email
sec_user = instance.get_user_by_email(SECONDARYMAIL) sec_user = instance.get_user_by_email(SECONDARYMAIL)
@ -334,18 +365,21 @@ def test_delete_repo_orgowned(instance):
def test_change_repo_ownership_org(instance): def test_change_repo_ownership_org(instance):
old_org = Organization.request(instance, test_org) old_org = Organization.request(instance, test_org)
user = User.request(instance, test_user) user = User.request(instance, test_user)
new_org = instance.create_org(user,test_org+"_repomove", "Org for testing moving repositories") new_org = instance.create_org(
user, test_org + "_repomove", "Org for testing moving repositories"
)
new_team = instance.create_team(new_org, test_team + "_repomove", "descr") new_team = instance.create_team(new_org, test_team + "_repomove", "descr")
repo_name = test_repo+"_repomove" repo_name = test_repo + "_repomove"
repo = instance.create_repo(old_org, repo_name , "descr") repo = instance.create_repo(old_org, repo_name, "descr")
repo.transfer_ownership(new_org, set([new_team])) repo.transfer_ownership(new_org, set([new_team]))
assert repo_name not in [repo.name for repo in old_org.get_repositories()] assert repo_name not in [repo.name for repo in old_org.get_repositories()]
assert repo_name in [repo.name for repo in new_org.get_repositories()] assert repo_name in [repo.name for repo in new_org.get_repositories()]
def test_change_repo_ownership_user(instance): def test_change_repo_ownership_user(instance):
old_org = Organization.request(instance, test_org) old_org = Organization.request(instance, test_org)
user = User.request(instance, test_user) user = User.request(instance, test_user)
repo_name = test_repo+"_repomove" repo_name = test_repo + "_repomove"
repo = instance.create_repo(old_org, repo_name, "descr") repo = instance.create_repo(old_org, repo_name, "descr")
repo.transfer_ownership(user) repo.transfer_ownership(user)
assert repo_name not in [repo.name for repo in old_org.get_repositories()] assert repo_name not in [repo.name for repo in old_org.get_repositories()]
@ -363,6 +397,7 @@ def test_delete_team(instance):
with pytest.raises(NotFoundException) as e: with pytest.raises(NotFoundException) as e:
team = org.get_team(test_team) team = org.get_team(test_team)
def test_delete_teams(instance): def test_delete_teams(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repos = org.get_repositories() repos = org.get_repositories()
@ -371,6 +406,7 @@ def test_delete_teams(instance):
repos = org.get_repositories() repos = org.get_repositories()
assert len(repos) == 0 assert len(repos) == 0
def test_delete_org(instance): def test_delete_org(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
org.delete() org.delete()

Wyświetl plik

@ -4,6 +4,7 @@ import uuid
from gitea import Gitea, User, Organization, Team, Repository, Issue from gitea import Gitea, User, Organization, Team, Repository, Issue
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
@pytest.fixture @pytest.fixture
def instance(scope="module"): def instance(scope="module"):
@ -20,15 +21,20 @@ def instance(scope="module"):
- Token at .token \ - Token at .token \
?" ?"
# make up some fresh names for the tests run # make up some fresh names for the tests run
test_org = "org_" + uuid.uuid4().hex[:8] test_org = "org_" + uuid.uuid4().hex[:8]
test_user = "user_" + uuid.uuid4().hex[:8] test_user = "user_" + uuid.uuid4().hex[:8]
test_team = "team_" + uuid.uuid4().hex[:8] # team names seem to have a rather low max lenght test_team = (
"team_" + uuid.uuid4().hex[:8]
) # team names seem to have a rather low max lenght
test_repo = "repo_" + uuid.uuid4().hex[:8] test_repo = "repo_" + uuid.uuid4().hex[:8]
def test_list_repos(instance): def test_list_repos(instance):
user = instance.create_user(test_user, test_user + "@example.org", "abcdefg1.23AB", send_notify=False) user = instance.create_user(
test_user, test_user + "@example.org", "abcdefg1.23AB", send_notify=False
)
org = instance.create_org(user, test_org, "some Description for longtests") org = instance.create_org(user, test_org, "some Description for longtests")
repos = org.get_repositories() repos = org.get_repositories()
assert len(repos) == 0 assert len(repos) == 0
@ -41,8 +47,15 @@ def test_list_repos(instance):
def test_list_issue(instance): def test_list_issue(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = instance.create_repo(org, test_repo, "Testing a huge number of Issues and how they are listed") repo = instance.create_repo(
org, test_repo, "Testing a huge number of Issues and how they are listed"
)
for x in range(0, 100): for x in range(0, 100):
Issue.create_issue(instance, repo, "TestIssue" + str(x), "We will be too many to be listed on one page") Issue.create_issue(
instance,
repo,
"TestIssue" + str(x),
"We will be too many to be listed on one page",
)
issues = repo.get_issues() issues = repo.get_issues()
assert len(issues) > 98 assert len(issues) > 98