mirror of
https://github.com/resf/distro-tools.git
synced 2024-11-22 05:01:26 +00:00
Add compat RSS endpoint
This commit is contained in:
parent
6eccd0968c
commit
988ac4d042
@ -1,7 +1,7 @@
|
|||||||
load("@aspect_rules_py//py:defs.bzl", "py_library")
|
load("@aspect_rules_py//py:defs.bzl", "py_library")
|
||||||
load("//build/macros:pyimage.bzl", "pyimage")
|
load("//build/macros:pyimage.bzl", "py_binary")
|
||||||
|
|
||||||
pyimage(
|
py_binary(
|
||||||
name = "rhworker",
|
name = "rhworker",
|
||||||
srcs = ["__main__.py"],
|
srcs = ["__main__.py"],
|
||||||
image_name = "apollo-rhworker",
|
image_name = "apollo-rhworker",
|
||||||
@ -28,7 +28,6 @@ py_library(
|
|||||||
deps = [
|
deps = [
|
||||||
"//apollo/db:db_lib",
|
"//apollo/db:db_lib",
|
||||||
"//apollo/rherrata:rherrata_lib",
|
"//apollo/rherrata:rherrata_lib",
|
||||||
"//apollo/rhwebscraper:rhwebscraper_lib",
|
|
||||||
"//common:common_lib",
|
"//common:common_lib",
|
||||||
"@pypi_aiohttp//:pkg",
|
"@pypi_aiohttp//:pkg",
|
||||||
"@pypi_temporalio//:pkg",
|
"@pypi_temporalio//:pkg",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
load("@aspect_rules_py//py:defs.bzl", "py_library")
|
load("@aspect_rules_py//py:defs.bzl", "py_library")
|
||||||
load("//build/macros:pyimage.bzl", "pyimage")
|
load("//build/macros:pyimage.bzl", "py_binary")
|
||||||
|
|
||||||
py_library(
|
py_library(
|
||||||
name = "rpmworker_lib",
|
name = "rpmworker_lib",
|
||||||
@ -21,10 +21,10 @@ py_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
pyimage(
|
py_binary(
|
||||||
name = "rpmworker",
|
name = "rpmworker",
|
||||||
image_name = "apollo-rpmworker",
|
|
||||||
srcs = ["__main__.py"],
|
srcs = ["__main__.py"],
|
||||||
|
image_name = "apollo-rpmworker",
|
||||||
imports = ["../.."],
|
imports = ["../.."],
|
||||||
main = "__main__.py",
|
main = "__main__.py",
|
||||||
visibility = ["//:__subpackages__"],
|
visibility = ["//:__subpackages__"],
|
||||||
|
@ -35,6 +35,7 @@ py_library(
|
|||||||
"@pypi_jinja2//:pkg",
|
"@pypi_jinja2//:pkg",
|
||||||
"@pypi_passlib//:pkg",
|
"@pypi_passlib//:pkg",
|
||||||
"@pypi_python_multipart//:pkg",
|
"@pypi_python_multipart//:pkg",
|
||||||
|
"@pypi_rssgen//:pkg",
|
||||||
"@pypi_starlette//:pkg",
|
"@pypi_starlette//:pkg",
|
||||||
"@pypi_tortoise_orm//:pkg",
|
"@pypi_tortoise_orm//:pkg",
|
||||||
],
|
],
|
||||||
|
@ -7,14 +7,17 @@ from typing import TypeVar, Generic, Optional
|
|||||||
|
|
||||||
from tortoise import connections
|
from tortoise import connections
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Query
|
from fastapi import APIRouter, Depends, Query, Response
|
||||||
from fastapi.exceptions import HTTPException
|
from fastapi.exceptions import HTTPException
|
||||||
from fastapi_pagination.links import Page
|
from fastapi_pagination.links import Page
|
||||||
from fastapi_pagination import Params
|
from fastapi_pagination import Params
|
||||||
from fastapi_pagination.ext.tortoise import create_page
|
from fastapi_pagination.ext.tortoise import create_page
|
||||||
|
|
||||||
|
from rssgen.feed import RssGenerator
|
||||||
|
|
||||||
from apollo.db import Advisory, RedHatIndexState
|
from apollo.db import Advisory, RedHatIndexState
|
||||||
from apollo.db.serialize import Advisory_Pydantic_V2, Advisory_Pydantic_V2_CVE, Advisory_Pydantic_V2_Fix
|
from apollo.db.serialize import Advisory_Pydantic_V2, Advisory_Pydantic_V2_CVE, Advisory_Pydantic_V2_Fix
|
||||||
|
from apollo.server.settings import UI_URL, COMPANY_NAME, MANAGING_EDITOR, get_setting
|
||||||
|
|
||||||
from common.fastapi import RenderErrorTemplateException
|
from common.fastapi import RenderErrorTemplateException
|
||||||
|
|
||||||
@ -31,6 +34,13 @@ class Pagination(Page[T], Generic[T]):
|
|||||||
fields = {"items": {"alias": "advisories"}}
|
fields = {"items": {"alias": "advisories"}}
|
||||||
|
|
||||||
|
|
||||||
|
class CompatParams(Params):
|
||||||
|
limit: int = Query(50, ge=1, le=100, description="Page size")
|
||||||
|
|
||||||
|
def get_size(self) -> int:
|
||||||
|
return self.limit if self.limit else self.size
|
||||||
|
|
||||||
|
|
||||||
def v3_advisory_to_v2(
|
def v3_advisory_to_v2(
|
||||||
advisory: Advisory,
|
advisory: Advisory,
|
||||||
include_rpms=True,
|
include_rpms=True,
|
||||||
@ -104,20 +114,16 @@ def v3_advisory_to_v2(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
async def fetch_advisories_compat(
|
||||||
"/",
|
params: CompatParams,
|
||||||
response_model=Pagination[Advisory_Pydantic_V2],
|
product: str,
|
||||||
)
|
before_raw: str,
|
||||||
async def list_advisories_compat_v2(
|
after_raw: str,
|
||||||
params: Params = Depends(),
|
cve: str,
|
||||||
product: str = Query(default=None, alias="filters.product"),
|
synopsis: str,
|
||||||
before_raw: str = Query(default=None, alias="filters.before"),
|
keyword: str,
|
||||||
after_raw: str = Query(default=None, alias="filters.after"),
|
severity: str,
|
||||||
cve: str = Query(default=None, alias="filters.cve"),
|
kind: str,
|
||||||
synopsis: str = Query(default=None, alias="filters.synopsis"),
|
|
||||||
keyword: str = Query(default=None, alias="filters.keyword"),
|
|
||||||
severity: str = Query(default=None, alias="filters.severity"),
|
|
||||||
kind: str = Query(default=None, alias="filters.type"),
|
|
||||||
):
|
):
|
||||||
before = None
|
before = None
|
||||||
after = None
|
after = None
|
||||||
@ -128,15 +134,13 @@ async def list_advisories_compat_v2(
|
|||||||
before_raw.removesuffix("Z")
|
before_raw.removesuffix("Z")
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
raise RenderErrorTemplateException("Invalid before date", 400)
|
raise RenderErrorTemplateException("Invalid before date", 400) # noqa # pylint: disable=raise-missing-from
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if after_raw:
|
if after_raw:
|
||||||
after = datetime.datetime.fromisoformat(after_raw.removesuffix("Z"))
|
after = datetime.datetime.fromisoformat(after_raw.removesuffix("Z"))
|
||||||
except:
|
except:
|
||||||
raise RenderErrorTemplateException("Invalid after date", 400)
|
raise RenderErrorTemplateException("Invalid after date", 400) # noqa # pylint: disable=raise-missing-from
|
||||||
|
|
||||||
state = await RedHatIndexState.first()
|
|
||||||
|
|
||||||
a = """
|
a = """
|
||||||
with vars (search, size, page_offset, product, before, after, cve, synopsis, severity, kind) as (
|
with vars (search, size, page_offset, product, before, after, cve, synopsis, severity, kind) as (
|
||||||
@ -157,11 +161,10 @@ async def list_advisories_compat_v2(
|
|||||||
count(a.*) over () as total
|
count(a.*) over () as total
|
||||||
from
|
from
|
||||||
advisories a
|
advisories a
|
||||||
left outer join advisory_affected_products ap on ap.advisory_id = a.id
|
|
||||||
left outer join advisory_cves c on c.advisory_id = a.id
|
left outer join advisory_cves c on c.advisory_id = a.id
|
||||||
left outer join advisory_fixes f on f.advisory_id = a.id
|
left outer join advisory_fixes f on f.advisory_id = a.id
|
||||||
where
|
where
|
||||||
((select product from vars) is null or ap.name ilike '%' || (select product from vars) || '%')
|
((select product from vars) is null or exists (select name from advisory_affected_products where advisory_id = a.id and name like '%' || (select product from vars) || '%'))
|
||||||
and ((select before from vars) is null or a.published_at < (select before from vars))
|
and ((select before from vars) is null or a.published_at < (select before from vars))
|
||||||
and ((select after from vars) is null or a.published_at > (select after from vars))
|
and ((select after from vars) is null or a.published_at > (select after from vars))
|
||||||
and (a.published_at is not null)
|
and (a.published_at is not null)
|
||||||
@ -170,7 +173,7 @@ async def list_advisories_compat_v2(
|
|||||||
and ((select severity from vars) is null or a.severity = (select severity from vars))
|
and ((select severity from vars) is null or a.severity = (select severity from vars))
|
||||||
and ((select kind from vars) is null or a.kind = (select kind from vars))
|
and ((select kind from vars) is null or a.kind = (select kind from vars))
|
||||||
and ((select search from vars) is null or
|
and ((select search from vars) is null or
|
||||||
ap.name ilike '%' || (select search from vars) || '%' or
|
exists (select name from advisory_affected_products where advisory_id = a.id and name like '%' || (select product from vars) || '%') or
|
||||||
a.synopsis ilike '%' || (select search from vars) || '%' or
|
a.synopsis ilike '%' || (select search from vars) || '%' or
|
||||||
a.description ilike '%' || (select search from vars) || '%' or
|
a.description ilike '%' || (select search from vars) || '%' or
|
||||||
exists (select cve from advisory_cves where advisory_id = a.id and cve ilike '%' || (select search from vars) || '%') or
|
exists (select cve from advisory_cves where advisory_id = a.id and cve ilike '%' || (select search from vars) || '%') or
|
||||||
@ -184,8 +187,16 @@ async def list_advisories_compat_v2(
|
|||||||
connection = connections.get("default")
|
connection = connections.get("default")
|
||||||
results = await connection.execute_query(
|
results = await connection.execute_query(
|
||||||
a, [
|
a, [
|
||||||
keyword, params.size, params.size * (params.page - 1), product,
|
keyword,
|
||||||
before, after, cve, synopsis, severity, kind
|
params.get_size(),
|
||||||
|
params.get_size() * (params.page - 1),
|
||||||
|
product,
|
||||||
|
before,
|
||||||
|
after,
|
||||||
|
cve,
|
||||||
|
synopsis,
|
||||||
|
severity,
|
||||||
|
kind,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -194,8 +205,42 @@ async def list_advisories_compat_v2(
|
|||||||
if results[1]:
|
if results[1]:
|
||||||
count = results[1][0]["total"]
|
count = results[1][0]["total"]
|
||||||
|
|
||||||
|
return (count, results[1])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(
|
||||||
|
"/",
|
||||||
|
response_model=Pagination[Advisory_Pydantic_V2],
|
||||||
|
)
|
||||||
|
async def list_advisories_compat_v2(
|
||||||
|
params: CompatParams = Depends(),
|
||||||
|
product: str = Query(default=None, alias="filters.product"),
|
||||||
|
before_raw: str = Query(default=None, alias="filters.before"),
|
||||||
|
after_raw: str = Query(default=None, alias="filters.after"),
|
||||||
|
cve: str = Query(default=None, alias="filters.cve"),
|
||||||
|
synopsis: str = Query(default=None, alias="filters.synopsis"),
|
||||||
|
keyword: str = Query(default=None, alias="filters.keyword"),
|
||||||
|
severity: str = Query(default=None, alias="filters.severity"),
|
||||||
|
kind: str = Query(default=None, alias="filters.type"),
|
||||||
|
):
|
||||||
|
|
||||||
|
state = await RedHatIndexState.first()
|
||||||
|
|
||||||
|
fetch_adv = await fetch_advisories_compat(
|
||||||
|
params,
|
||||||
|
product,
|
||||||
|
before_raw,
|
||||||
|
after_raw,
|
||||||
|
cve,
|
||||||
|
synopsis,
|
||||||
|
keyword,
|
||||||
|
severity,
|
||||||
|
kind,
|
||||||
|
)
|
||||||
|
count = fetch_adv[0]
|
||||||
|
|
||||||
advisories = []
|
advisories = []
|
||||||
for adv in results[1]:
|
for adv in fetch_adv[1]:
|
||||||
advisory = Advisory(**adv)
|
advisory = Advisory(**adv)
|
||||||
await advisory.fetch_related(
|
await advisory.fetch_related(
|
||||||
"packages",
|
"packages",
|
||||||
@ -220,6 +265,62 @@ async def list_advisories_compat_v2(
|
|||||||
return page
|
return page
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(":rss")
|
||||||
|
async def list_advisories_compat_v2_rss(
|
||||||
|
params: CompatParams = Depends(),
|
||||||
|
product: str = Query(default=None, alias="filters.product"),
|
||||||
|
before_raw: str = Query(default=None, alias="filters.before"),
|
||||||
|
after_raw: str = Query(default=None, alias="filters.after"),
|
||||||
|
cve: str = Query(default=None, alias="filters.cve"),
|
||||||
|
synopsis: str = Query(default=None, alias="filters.synopsis"),
|
||||||
|
keyword: str = Query(default=None, alias="filters.keyword"),
|
||||||
|
severity: str = Query(default=None, alias="filters.severity"),
|
||||||
|
kind: str = Query(default=None, alias="filters.type"),
|
||||||
|
):
|
||||||
|
fetch_adv = await fetch_advisories_compat(
|
||||||
|
params,
|
||||||
|
product,
|
||||||
|
before_raw,
|
||||||
|
after_raw,
|
||||||
|
cve,
|
||||||
|
synopsis,
|
||||||
|
keyword,
|
||||||
|
severity,
|
||||||
|
kind,
|
||||||
|
)
|
||||||
|
count = fetch_adv[0]
|
||||||
|
advisories = fetch_adv[1]
|
||||||
|
|
||||||
|
ui_url = await get_setting(UI_URL)
|
||||||
|
company_name = await get_setting(COMPANY_NAME)
|
||||||
|
managing_editor = await get_setting(MANAGING_EDITOR)
|
||||||
|
|
||||||
|
fg = RssGenerator()
|
||||||
|
fg.title(f"{company_name} Errata Feed")
|
||||||
|
fg.link(href=ui_url, rel="alternate")
|
||||||
|
fg.language("en")
|
||||||
|
fg.description(f"Advisories issued by {company_name}")
|
||||||
|
fg.copyright(
|
||||||
|
f"(C) {company_name} {datetime.datetime.now().year}. All rights reserved. CVE sources are copyright of their respective owners."
|
||||||
|
)
|
||||||
|
fg.managingEditor(f"{managing_editor} ({company_name})")
|
||||||
|
|
||||||
|
if count != 0:
|
||||||
|
fg.pubDate(advisories[0]["published_at"])
|
||||||
|
fg.lastBuildDate(advisories[0]["published_at"])
|
||||||
|
|
||||||
|
for adv in advisories:
|
||||||
|
advisory = Advisory(**adv)
|
||||||
|
fe = fg.add_entry()
|
||||||
|
fe.title(f"{advisory.name}: {advisory.synopsis}")
|
||||||
|
fe.link(href=f"{ui_url}/{advisory.name}", rel="alternate")
|
||||||
|
fe.description(advisory.topic)
|
||||||
|
fe.id(str(advisory.id))
|
||||||
|
fe.pubDate(advisory.published_at)
|
||||||
|
|
||||||
|
return Response(content=fg.rss_str(), media_type="application/xml")
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/{advisory_name}",
|
"/{advisory_name}",
|
||||||
response_model=Advisory_Pydantic_V2,
|
response_model=Advisory_Pydantic_V2,
|
||||||
|
@ -16,6 +16,9 @@ OIDC_ADMIN_ROLE = "oidc-admin-role"
|
|||||||
OIDC_ELEVATED_ROLE = "oidc-elevated-role"
|
OIDC_ELEVATED_ROLE = "oidc-elevated-role"
|
||||||
RH_MATCH_STALE = "rh-match-stale"
|
RH_MATCH_STALE = "rh-match-stale"
|
||||||
DISABLE_SERVING_RH_ADVISORIES = "disable-serving-rh-advisories"
|
DISABLE_SERVING_RH_ADVISORIES = "disable-serving-rh-advisories"
|
||||||
|
UI_URL = "ui-url"
|
||||||
|
COMPANY_NAME = "company-name"
|
||||||
|
MANAGING_EDITOR = "managing-editor"
|
||||||
|
|
||||||
|
|
||||||
async def get_setting(name: str) -> Optional[str]:
|
async def get_setting(name: str) -> Optional[str]:
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
load("@aspect_rules_py//py:defs.bzl", "py_binary")
|
load("@aspect_rules_py//py:defs.bzl", _py_binary = "py_binary")
|
||||||
load("@io_bazel_rules_docker//python3:image.bzl", "py3_image")
|
load("@io_bazel_rules_docker//python3:image.bzl", "py3_image")
|
||||||
load("@io_bazel_rules_docker//container:container.bzl", "container_push")
|
load("@io_bazel_rules_docker//container:container.bzl", "container_push")
|
||||||
|
|
||||||
def pyimage(name, image_name, **kwargs):
|
def py_binary(name, image_name, **kwargs):
|
||||||
py_binary(
|
_py_binary(
|
||||||
name = name,
|
name = name,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
@ -1069,6 +1069,12 @@ manifest:
|
|||||||
rfc3986.parseresult: rfc3986
|
rfc3986.parseresult: rfc3986
|
||||||
rfc3986.uri: rfc3986
|
rfc3986.uri: rfc3986
|
||||||
rfc3986.validators: rfc3986
|
rfc3986.validators: rfc3986
|
||||||
|
rssgen: rssgen
|
||||||
|
rssgen.compat: rssgen
|
||||||
|
rssgen.entry: rssgen
|
||||||
|
rssgen.feed: rssgen
|
||||||
|
rssgen.util: rssgen
|
||||||
|
rssgen.version: rssgen
|
||||||
setuptools: setuptools
|
setuptools: setuptools
|
||||||
setuptools.archive_util: setuptools
|
setuptools.archive_util: setuptools
|
||||||
setuptools.build_meta: setuptools
|
setuptools.build_meta: setuptools
|
||||||
@ -1497,4 +1503,4 @@ manifest:
|
|||||||
yarl: yarl
|
yarl: yarl
|
||||||
pip_repository:
|
pip_repository:
|
||||||
name: pypi
|
name: pypi
|
||||||
integrity: 6adcf30189668fa8123faef9b05219d91bf45a1950bc648c64a3c84e829b117d
|
integrity: 98955591e0f143193fb26aa58a3c5c9120c83f3270459709acd849f9613119ac
|
||||||
|
@ -16,4 +16,5 @@ passlib[bcrypt]==1.7.4
|
|||||||
python-multipart==0.0.5
|
python-multipart==0.0.5
|
||||||
itsdangerous==2.1.2
|
itsdangerous==2.1.2
|
||||||
PyYAML==6.0
|
PyYAML==6.0
|
||||||
beautifulsoup4==4.11.2
|
beautifulsoup4==4.11.2
|
||||||
|
rssgen==0.9.0
|
@ -648,6 +648,7 @@ python-dateutil==2.8.2 \
|
|||||||
--hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
|
--hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
|
||||||
# via
|
# via
|
||||||
# openapi-python-client
|
# openapi-python-client
|
||||||
|
# rssgen
|
||||||
# temporalio
|
# temporalio
|
||||||
python-multipart==0.0.5 \
|
python-multipart==0.0.5 \
|
||||||
--hash=sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43
|
--hash=sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43
|
||||||
@ -704,6 +705,10 @@ rfc3986[idna2008]==1.5.0 \
|
|||||||
--hash=sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835 \
|
--hash=sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835 \
|
||||||
--hash=sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97
|
--hash=sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97
|
||||||
# via httpx
|
# via httpx
|
||||||
|
rssgen==0.9.0 \
|
||||||
|
--hash=sha256:26b6ec12d59a55b9962b83d4044e3bf9cd2d11c4942d8e93692eac70b9f3f6fa \
|
||||||
|
--hash=sha256:c8c19bc1540a5789221e2df0be591d5864e619c49ab6517a8f4a524f8b7be868
|
||||||
|
# via -r ./requirements.txt
|
||||||
setuptools==58.2.0 \
|
setuptools==58.2.0 \
|
||||||
--hash=sha256:2551203ae6955b9876741a26ab3e767bb3242dafe86a32a749ea0d78b6792f11 \
|
--hash=sha256:2551203ae6955b9876741a26ab3e767bb3242dafe86a32a749ea0d78b6792f11 \
|
||||||
--hash=sha256:2c55bdb85d5bb460bd2e3b12052b677879cffcf46c0c688f2e5bf51d36001145
|
--hash=sha256:2c55bdb85d5bb460bd2e3b12052b677879cffcf46c0c688f2e5bf51d36001145
|
||||||
|
Loading…
Reference in New Issue
Block a user