mirror of
https://github.com/revanced/revanced-releases-api.git
synced 2025-04-30 14:34:28 +02:00
removes /app, adds /contributors and cleans-up code
This commit is contained in:
parent
6d9a6d0ff6
commit
c0f7210725
@ -10,9 +10,9 @@ The team also have a [Discord Server](https://revanced.app/discord) if you need
|
||||
|
||||
## API Endpoints
|
||||
|
||||
* [apps](/apps) - Returns all currently patchable apps
|
||||
* [tools](/tools) - Returns the latest version of all ReVanced tools and Vanced MicroG
|
||||
* [patches](/patches) - Returns the latest version of all ReVanced patches
|
||||
* [contributors](/contributors) - Returns contributors for all ReVanced projects
|
||||
|
||||
## Additional Information
|
||||
|
||||
@ -28,7 +28,7 @@ The team also have a [Discord Server](https://revanced.app/discord) if you need
|
||||
Godspeed 💀
|
||||
|
||||
"""
|
||||
version = "0.1 alpha"
|
||||
version = "0.2 alpha"
|
||||
|
||||
[license]
|
||||
|
||||
@ -49,4 +49,4 @@ expire = 60
|
||||
|
||||
[app]
|
||||
|
||||
repositories = ["TeamVanced/VancedMicroG", "revanced/revanced-cli", "revanced/revanced-patches", "revanced/revanced-integrations"]
|
||||
repositories = ["TeamVanced/VancedMicroG", "revanced/revanced-cli", "revanced/revanced-patches", "revanced/revanced-integrations", "revanced/revanced-manager"]
|
22
main.py
22
main.py
@ -77,17 +77,6 @@ async def tools(request: Request, response: Response) -> dict:
|
||||
"""
|
||||
return await releases.get_latest_releases(config['app']['repositories'])
|
||||
|
||||
@app.get('/apps', response_model=ResponseModels.AppsResponseModel)
|
||||
@limiter.limit(config['slowapi']['limit'])
|
||||
@cache(config['cache']['expire'])
|
||||
async def apps(request: Request, response: Response) -> dict:
|
||||
"""Get patchable apps.
|
||||
|
||||
Returns:
|
||||
json: list of supported apps
|
||||
"""
|
||||
return await releases.get_patchable_apps()
|
||||
|
||||
@app.get('/patches', response_model=ResponseModels.PatchesResponseModel)
|
||||
@limiter.limit(config['slowapi']['limit'])
|
||||
@cache(config['cache']['expire'])
|
||||
@ -100,6 +89,17 @@ async def patches(request: Request, response: Response) -> dict:
|
||||
|
||||
return await releases.get_patches_json()
|
||||
|
||||
@app.get('/contributors', response_model=ResponseModels.ContributorsResponseModel)
|
||||
@limiter.limit(config['slowapi']['limit'])
|
||||
@cache(config['cache']['expire'])
|
||||
async def contributors(request: Request, response: Response) -> dict:
|
||||
"""Get contributors.
|
||||
|
||||
Returns:
|
||||
json: list of contributors
|
||||
"""
|
||||
return await releases.get_contributors(config['app']['repositories'])
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup() -> None:
|
||||
redis_url = f"{redis_config['url']}:{redis_config['port']}/{redis_config['collection']}"
|
||||
|
@ -12,33 +12,34 @@ class Releases:
|
||||
'Authorization': "token " + os.environ['GITHUB_TOKEN']
|
||||
}
|
||||
|
||||
async def get_release(self, client: httpx_cache.AsyncClient, repository: str) -> list:
|
||||
"""Get assets from latest release in a given repository.
|
||||
async def _get_release(self, client: httpx_cache.AsyncClient, repository: str) -> list:
|
||||
# Get assets from latest release in a given repository.
|
||||
#
|
||||
# Args:
|
||||
# client (httpx_cache.AsyncClient): httpx_cache reusable async client
|
||||
# repository (str): Github's standard username/repository notation
|
||||
#
|
||||
# Returns:
|
||||
# dict: dictionary of filename and download url
|
||||
|
||||
Args:
|
||||
client (httpx_cache.AsyncClient): httpx_cache reusable async client
|
||||
repository (str): Github's standard username/repository notation
|
||||
|
||||
Returns:
|
||||
dict: dictionary of filename and download url
|
||||
"""
|
||||
assets = []
|
||||
response = await client.get(f"https://api.github.com/repos/{repository}/releases/latest")
|
||||
|
||||
release_assets = response.json()['assets']
|
||||
if response.status_code == 200:
|
||||
release_assets = response.json()['assets']
|
||||
|
||||
for asset in release_assets:
|
||||
assets.append({ 'repository': repository,
|
||||
'name': asset['name'],
|
||||
'size': asset['size'],
|
||||
'browser_download_url': asset['browser_download_url'],
|
||||
'content_type': asset['content_type']
|
||||
})
|
||||
for asset in release_assets:
|
||||
assets.append({ 'repository': repository,
|
||||
'name': asset['name'],
|
||||
'size': asset['size'],
|
||||
'browser_download_url': asset['browser_download_url'],
|
||||
'content_type': asset['content_type']
|
||||
})
|
||||
|
||||
return assets
|
||||
|
||||
async def get_latest_releases(self, repositories: list) -> dict:
|
||||
"""Runs get_release() in parallel for each repository.
|
||||
"""Runs get_release() asynchronously for each repository.
|
||||
|
||||
Args:
|
||||
repositories (list): List of repositories in Github's standard username/repository notation
|
||||
@ -46,46 +47,19 @@ class Releases:
|
||||
Returns:
|
||||
dict: A dictionary containing assets from each repository
|
||||
"""
|
||||
|
||||
releases: Dict[str, List] = {}
|
||||
releases['tools'] = []
|
||||
|
||||
async with httpx_cache.AsyncClient(headers=self.headers, http2=True) as client:
|
||||
for repository in repositories:
|
||||
files = await self.get_release(client, repository)
|
||||
for file in files:
|
||||
releases['tools'].append(file)
|
||||
files = await self._get_release(client, repository)
|
||||
if files:
|
||||
for file in files:
|
||||
releases['tools'].append(file)
|
||||
|
||||
return releases
|
||||
|
||||
async def _get_patches_readme(self, client: httpx_cache.AsyncClient) -> str:
|
||||
# Get revanced-patches repository's README.md.
|
||||
#
|
||||
# Returns:
|
||||
# str: README.md content
|
||||
#
|
||||
|
||||
response = await client.get(f"https://api.github.com/repos/revanced/revanced-patches/contents/README.md")
|
||||
|
||||
return b64decode(response.json()['content']).decode('utf-8')
|
||||
|
||||
async def get_patchable_apps(self) -> dict:
|
||||
"""Get patchable apps from revanced-patches repository.
|
||||
|
||||
Returns:
|
||||
dict: Apps available for patching
|
||||
"""
|
||||
packages: Dict[str, List] = {}
|
||||
packages['apps'] = []
|
||||
|
||||
async with httpx_cache.AsyncClient(headers=self.headers, http2=True) as client:
|
||||
content = await self._get_patches_readme(client)
|
||||
|
||||
for line in content.splitlines():
|
||||
if line.startswith(u'###'):
|
||||
packages['apps'].append(line.split('`')[1])
|
||||
|
||||
return packages
|
||||
|
||||
async def _get_patches_json(self, client: httpx_cache.AsyncClient) -> dict:
|
||||
# Get revanced-patches repository's README.md.
|
||||
#
|
||||
@ -106,6 +80,7 @@ class Releases:
|
||||
Returns:
|
||||
dict: Patches available for a given app
|
||||
"""
|
||||
|
||||
async def generate_simplified_json(payload: dict) -> dict:
|
||||
return {}
|
||||
|
||||
@ -116,3 +91,39 @@ class Releases:
|
||||
return await generate_simplified_json(content)
|
||||
|
||||
return content
|
||||
|
||||
async def _get_contributors(self, client: httpx_cache.AsyncClient, repository: str) -> list:
|
||||
# Get contributors from a given repository.
|
||||
#
|
||||
# Args:
|
||||
# client (httpx_cache.AsyncClient): httpx_cache reusable async client
|
||||
# repository (str): Github's standard username/repository notation
|
||||
#
|
||||
# Returns:
|
||||
# list: a list of dictionaries containing the repository's contributors
|
||||
|
||||
response = await client.get(f"https://api.github.com/repos/{repository}/contributors")
|
||||
|
||||
return response.json()
|
||||
|
||||
async def get_contributors(self, repositories: list) -> dict:
|
||||
"""Runs get_contributors() asynchronously for each repository.
|
||||
|
||||
Args:
|
||||
repositories (list): List of repositories in Github's standard username/repository notation
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the contributors from each repository
|
||||
"""
|
||||
|
||||
contributors: Dict[str, List] = {}
|
||||
contributors['repositories'] = []
|
||||
|
||||
async with httpx_cache.AsyncClient(headers=self.headers, http2=True) as client:
|
||||
for repository in repositories:
|
||||
if 'revanced' in repository:
|
||||
repo_contributors = await self._get_contributors(client, repository)
|
||||
data = { 'name': repository, 'contributors': repo_contributors }
|
||||
contributors['repositories'].append(data)
|
||||
|
||||
return contributors
|
@ -31,3 +31,39 @@ class PatchesResponseFields(BaseModel):
|
||||
excluded: bool
|
||||
dependencies: list[ str ] | None
|
||||
compatiblePackages: list[ CompatiblePackagesResponseFields ]
|
||||
|
||||
class ContributorFields(BaseModel):
|
||||
"""Implements the fields for each contributor in the /contributors endpoint.
|
||||
|
||||
Args:
|
||||
BaseModel (pydantic.BaseModel): BaseModel from pydantic
|
||||
"""
|
||||
login: str
|
||||
id: str
|
||||
node_id: str
|
||||
avatar_url: str
|
||||
gravatar_id: str
|
||||
url: str
|
||||
html_url: str
|
||||
followers_url: str
|
||||
following_url: str
|
||||
gists_url: str
|
||||
starred_url: str
|
||||
subscriptions_url: str
|
||||
organizations_url: str
|
||||
repos_url: str
|
||||
events_url: str
|
||||
received_events_url: str
|
||||
type: str
|
||||
site_admin: str
|
||||
contributions: int
|
||||
|
||||
class ContributorsResponseFields(BaseModel):
|
||||
"""Implements the fields for each repository in the /contributors endpoint
|
||||
|
||||
Args:
|
||||
BaseModel (pydantic.BaseModel): BaseModel from pydantic
|
||||
"""
|
||||
|
||||
name: str
|
||||
contributors: list[ ContributorFields ]
|
@ -1,48 +1,8 @@
|
||||
import modules.ResponseFields as ResponseFields
|
||||
from pydantic import BaseModel, create_model
|
||||
from pydantic import BaseModel
|
||||
|
||||
"""Implements pydantic models and model generator for the API's responses."""
|
||||
|
||||
class ModelGenerator():
|
||||
|
||||
"""Generates a pydantic model from a dictionary."""
|
||||
|
||||
def __make_model(self, v, name):
|
||||
# Parses a dictionary and creates a pydantic model from it.
|
||||
#
|
||||
# Args:
|
||||
# v: key value
|
||||
# name: key name
|
||||
#
|
||||
# Returns:
|
||||
# pydantic.BaseModel: Generated pydantic model
|
||||
|
||||
if type(v) is dict:
|
||||
return create_model(name, **{k: self.__make_model(v, k) for k, v in v.items()}), ...
|
||||
return None, v
|
||||
|
||||
def generate(self, v: dict, name: str):
|
||||
|
||||
"""Returns a pydantic model from a dictionary.
|
||||
|
||||
Args:
|
||||
v (Dict): JSON dictionary
|
||||
name (str): Model name
|
||||
|
||||
Returns:
|
||||
pydantic.BaseModel: Generated pydantic model
|
||||
"""
|
||||
return self.__make_model(v, name)[0]
|
||||
|
||||
class AppsResponseModel(BaseModel):
|
||||
"""Implements the JSON response model for the /apps endpoint.
|
||||
|
||||
Args:
|
||||
BaseModel (pydantic.BaseModel): BaseModel from pydantic
|
||||
"""
|
||||
|
||||
apps: list[str]
|
||||
|
||||
class ToolsResponseModel(BaseModel):
|
||||
"""Implements the JSON response model for the /tools endpoint.
|
||||
|
||||
@ -60,3 +20,12 @@ class PatchesResponseModel(BaseModel):
|
||||
"""
|
||||
|
||||
__root__: list[ ResponseFields.PatchesResponseFields ]
|
||||
|
||||
class ContributorsResponseModel(BaseModel):
|
||||
"""Implements the JSON response model for the /contributors endpoint.
|
||||
|
||||
Args:
|
||||
BaseModel (pydantic.BaseModel): BaseModel from pydantic
|
||||
"""
|
||||
|
||||
repositories: list[ ResponseFields.ContributorsResponseFields ]
|
Loading…
x
Reference in New Issue
Block a user