Skip to content

Commit

Permalink
Add: Add async GitHub API to search for repositories (#557)
Browse files Browse the repository at this point in the history
**What**:

Implement the GitHub REST search repositories API.

**Why**:

Allow to search for a repo. All other repo related APIs require client
side filtering.
  • Loading branch information
bjoernricks committed Jan 16, 2023
2 parents 2fd4271 + 9a22b10 commit b1ccca5
Show file tree
Hide file tree
Showing 7 changed files with 912 additions and 2 deletions.
10 changes: 9 additions & 1 deletion pontos/github/api/api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2022 Greenbone Networks GmbH
# Copyright (C) 2022 - 2023 Greenbone Networks GmbH
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
Expand Down Expand Up @@ -58,6 +58,7 @@
GitHubRESTReleaseMixin,
)
from pontos.github.api.repositories import GitHubAsyncRESTRepositories
from pontos.github.api.search import GitHubAsyncRESTSearch
from pontos.github.api.tags import GitHubAsyncRESTTags
from pontos.github.api.teams import GitHubAsyncRESTTeams
from pontos.github.api.workflows import (
Expand Down Expand Up @@ -170,6 +171,13 @@ def tags(self) -> GitHubAsyncRESTTags:
"""
return GitHubAsyncRESTTags(self._client)

@property
def search(self) -> GitHubAsyncRESTSearch:
"""
Search related API
"""
return GitHubAsyncRESTSearch(self._client)

async def __aenter__(self) -> "GitHubAsyncRESTApi":
await self._client.__aenter__()
return self
Expand Down
70 changes: 70 additions & 0 deletions pontos/github/api/search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (C) 2023 Greenbone Networks GmbH
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from typing import AsyncIterator, Iterable, Union

from pontos.github.api.client import GitHubAsyncREST
from pontos.github.models.organization import Repository
from pontos.github.models.search import Qualifier, RepositorySort, SortOrder
from pontos.helper import enum_or_value


class GitHubAsyncRESTSearch(GitHubAsyncREST):
async def repositories(
self,
*,
keywords: Iterable[str],
qualifiers: Iterable[Qualifier],
order: Union[str, SortOrder] = SortOrder.DESC,
sort: Union[str, RepositorySort, None] = None,
) -> AsyncIterator[Repository]:
"""
Search for repositories
https://docs.github.com/en/rest/search#search-repositories
https://docs.github.com/en/search-github/searching-on-github/searching-for-repositories
Args:
keywords: List of keywords to search for.
qualifiers: List of qualifiers.
order: Sort order either 'asc' or 'desc'. Default is 'desc'.
sort: Sort the found repositories by this criteria.
Raises:
`httpx.HTTPStatusError` if there was an error in the request
"""
api = "/search/repositories"
params = {
"per_page": "100",
}
if order:
params["order"] = enum_or_value(order)
if sort:
params["sort"] = enum_or_value(sort)

query = (
f"{' '.join(keywords)} "
f"{' '.join([str(qualifier) for qualifier in qualifiers])}"
)
params["q"] = query

async for response in self._client.get_all(api, params=params):
response.raise_for_status()
data = response.json()
for repo in data["items"]:
yield Repository.from_dict(repo)
3 changes: 2 additions & 1 deletion pontos/github/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2022 Greenbone Networks GmbH
# Copyright (C) 2022 - 2023 Greenbone Networks GmbH
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
Expand All @@ -21,5 +21,6 @@
from pontos.github.models.organization import *
from pontos.github.models.pull_request import *
from pontos.github.models.release import *
from pontos.github.models.search import *
from pontos.github.models.tag import *
from pontos.github.models.workflow import *
133 changes: 133 additions & 0 deletions pontos/github/models/search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Copyright (C) 2023 Greenbone Networks GmbH
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

__all__ = (
"InDescriptionQualifier",
"InNameQualifier",
"InReadmeQualifier",
"InTopicsQualifier",
"IsPrivateQualifier",
"IsPublicQualifier",
"NotQualifier",
"OrganizationQualifier",
"Qualifier",
"RepositoryQualifier",
"RepositorySort",
"SortOrder",
"UserQualifier",
)

from abc import ABC
from enum import Enum


class SortOrder(Enum):
ASC = "asc"
DESC = "desc"


class RepositorySort(Enum):
STARS = "stars"
FORKS = "forks"
HELP_WANTED_ISSUES = "help-wanted-issues"
UPDATED = "updated"


class Qualifier(ABC):
operator: str
term: str

def __str__(self) -> str:
""" """
return f"{self.operator}:{self.term}"


class NotQualifier(Qualifier):
def __init__(self, qualifier: Qualifier) -> None:
self.qualifier = qualifier

def __str__(self) -> str:
return f"-{str(self.qualifier)}"


class InQualifier(Qualifier):
operator = "in"


class InNameQualifier(InQualifier):
term = "name"


class InDescriptionQualifier(InQualifier):
term = "description"


class InTopicsQualifier(InQualifier):
term = "topics"


class InReadmeQualifier(InQualifier):
term = "readme"


class RepositoryQualifier(Qualifier):
operator = "repo"

def __init__(self, repository: str) -> None:
"""
Search within a repository
Args:
repository: owner/repo
"""
self.term = repository


class OrganizationQualifier(Qualifier):
operator = "org"

def __init__(self, organization: str) -> None:
"""
Search within an organization
Args:
organization: Name of the organization to search within
"""
self.term = organization


class UserQualifier(Qualifier):
operator = "user"

def __init__(self, user: str) -> None:
"""
Search within an user space
Args:
user: Name of the user
"""
self.term = user


class IsPublicQualifier(Qualifier):
operator = "is"
term = "public"


class IsPrivateQualifier(Qualifier):
operator = "is"
term = "private"
Loading

0 comments on commit b1ccca5

Please sign in to comment.