This is an automated email from the ASF dual-hosted git repository.

tn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-atr-experiments.git


The following commit(s) were added to refs/heads/main by this push:
     new b08a2a3  add api blueprint, integrate quart-schema to generate an 
openapi definition for the api blueprint
b08a2a3 is described below

commit b08a2a3073bc4fb050efc220a357b746c57fed1f
Author: Thomas Neidhart <t...@apache.org>
AuthorDate: Mon Feb 17 22:21:51 2025 +0100

    add api blueprint, integrate quart-schema to generate an openapi definition 
for the api blueprint
---
 atr/blueprints/__init__.py                 |  2 +-
 atr/blueprints/{ => api}/__init__.py       | 16 ++------------
 atr/blueprints/{__init__.py => api/api.py} | 22 +++++++++----------
 atr/server.py                              | 17 +++++++++++++++
 atr/templates/pages.html                   | 14 ++++++++++++
 poetry.lock                                | 35 +++++++++++++++++++++++++++++-
 pyproject.toml                             |  3 ++-
 uv.lock                                    | 24 ++++++++++++++++++++
 8 files changed, 105 insertions(+), 28 deletions(-)

diff --git a/atr/blueprints/__init__.py b/atr/blueprints/__init__.py
index d5ccb09..4d8d1cf 100644
--- a/atr/blueprints/__init__.py
+++ b/atr/blueprints/__init__.py
@@ -20,7 +20,7 @@ from importlib.util import find_spec
 
 from asfquart.base import QuartApp
 
-_BLUEPRINT_MODULES = ["secret"]
+_BLUEPRINT_MODULES = ["api", "secret"]
 
 
 def register_blueprints(app: QuartApp) -> None:
diff --git a/atr/blueprints/__init__.py b/atr/blueprints/api/__init__.py
similarity index 62%
copy from atr/blueprints/__init__.py
copy to atr/blueprints/api/__init__.py
index d5ccb09..9831705 100644
--- a/atr/blueprints/__init__.py
+++ b/atr/blueprints/api/__init__.py
@@ -15,18 +15,6 @@
 # specific language governing permissions and limitations
 # under the License.
 
-from importlib import import_module
-from importlib.util import find_spec
+from quart import Blueprint
 
-from asfquart.base import QuartApp
-
-_BLUEPRINT_MODULES = ["secret"]
-
-
-def register_blueprints(app: QuartApp) -> None:
-    for routes_name in _BLUEPRINT_MODULES:
-        routes_fqn = f"atr.blueprints.{routes_name}.{routes_name}"
-        spec = find_spec(routes_fqn)
-        if spec is not None:
-            module = import_module(routes_fqn)
-            app.register_blueprint(module.blueprint)
+blueprint = Blueprint("api_blueprint", __name__, url_prefix="/api")
diff --git a/atr/blueprints/__init__.py b/atr/blueprints/api/api.py
similarity index 62%
copy from atr/blueprints/__init__.py
copy to atr/blueprints/api/api.py
index d5ccb09..0dac16f 100644
--- a/atr/blueprints/__init__.py
+++ b/atr/blueprints/api/api.py
@@ -15,18 +15,18 @@
 # specific language governing permissions and limitations
 # under the License.
 
-from importlib import import_module
-from importlib.util import find_spec
+from collections.abc import Mapping
+from typing import Any
 
-from asfquart.base import QuartApp
+from atr.db.service import get_pmc_by_name
 
-_BLUEPRINT_MODULES = ["secret"]
+from . import blueprint
 
 
-def register_blueprints(app: QuartApp) -> None:
-    for routes_name in _BLUEPRINT_MODULES:
-        routes_fqn = f"atr.blueprints.{routes_name}.{routes_name}"
-        spec = find_spec(routes_fqn)
-        if spec is not None:
-            module = import_module(routes_fqn)
-            app.register_blueprint(module.blueprint)
+@blueprint.route("/pmc/<project_name>")
+async def api_pmc(project_name: str) -> tuple[Mapping[str, Any], int]:
+    pmc = await get_pmc_by_name(project_name)
+    if pmc:
+        return pmc.model_dump(), 200
+    else:
+        return {}, 404
diff --git a/atr/server.py b/atr/server.py
index 738a12b..2203d78 100644
--- a/atr/server.py
+++ b/atr/server.py
@@ -19,8 +19,11 @@
 
 import logging
 import os
+from collections.abc import Iterable
 
 from decouple import config
+from quart_schema import OpenAPIProvider, QuartSchema
+from werkzeug.routing import Rule
 
 import asfquart
 import asfquart.generics
@@ -35,6 +38,13 @@ asfquart.generics.OAUTH_URL_INIT = 
"https://oauth.apache.org/auth?state=%s&redir
 asfquart.generics.OAUTH_URL_CALLBACK = "https://oauth.apache.org/token?code=%s";
 
 
+class ApiOnlyOpenAPIProvider(OpenAPIProvider):
+    def generate_rules(self) -> Iterable[Rule]:
+        for rule in super().generate_rules():
+            if rule.rule.startswith("/api"):
+                yield rule
+
+
 def register_routes() -> str:
     from . import routes
 
@@ -48,6 +58,13 @@ def create_app(app_config: type[AppConfig]) -> QuartApp:
     app = asfquart.construct(__name__)
     app.config.from_object(app_config)
 
+    QuartSchema(
+        app,
+        openapi_provider_class=ApiOnlyOpenAPIProvider,
+        swagger_ui_path="/api/docs",
+        openapi_path="/api/openapi.json",
+    )
+
     # # Configure static folder path before changing working directory
     # app.static_folder = 
os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")
 
diff --git a/atr/templates/pages.html b/atr/templates/pages.html
index 4fc6127..13eee25 100644
--- a/atr/templates/pages.html
+++ b/atr/templates/pages.html
@@ -221,6 +221,20 @@
         </div>
       </div>
     </div>
+
+    <div class="endpoint-group">
+      <h2>API</h2>
+
+      <div class="endpoint">
+        <h3>
+          <a href="{{ url_for('swagger_ui') }}">/api/docs</a>
+        </h3>
+        <div class="endpoint-description">Swagger UI.</div>
+        <div class="endpoint-meta">
+          Access: <span class="access-requirement public">Public</span>
+        </div>
+      </div>
+    </div>
   </div>
 
 {% endblock content %}
diff --git a/poetry.lock b/poetry.lock
index 163468d..934169a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1831,6 +1831,18 @@ files = [
 [package.dependencies]
 typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
 
+[[package]]
+name = "pyhumps"
+version = "3.8.0"
+description = "🐫  Convert strings (and dictionary keys) between snake case, 
camel case and pascal case in Python. Inspired by Humps for Node"
+optional = false
+python-versions = "*"
+groups = ["main"]
+files = [
+    {file = "pyhumps-3.8.0-py3-none-any.whl", hash = 
"sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6"},
+    {file = "pyhumps-3.8.0.tar.gz", hash = 
"sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"},
+]
+
 [[package]]
 name = "pyright"
 version = "1.1.394"
@@ -2043,6 +2055,27 @@ werkzeug = ">=3.0"
 [package.extras]
 dotenv = ["python-dotenv"]
 
+[[package]]
+name = "quart-schema"
+version = "0.21.0"
+description = "A Quart extension to provide schema validation"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+files = [
+    {file = "quart_schema-0.21.0-py3-none-any.whl", hash = 
"sha256:57b8f7d2f8cc5bcbf6b2200ed17f870fc26811c5d0ac5c6a02352644ab068129"},
+    {file = "quart_schema-0.21.0.tar.gz", hash = 
"sha256:5edcd18adb223c9d0f234688238882884fb09a4ee52fb29b50928821adbb7064"},
+]
+
+[package.dependencies]
+pyhumps = ">=1.6.1"
+quart = ">=0.19.0"
+
+[package.extras]
+docs = ["pydata_sphinx_theme", "sphinx-tabs (>=3.4.4)"]
+msgspec = ["msgspec (>=0.18)"]
+pydantic = ["pydantic (>=2)"]
+
 [[package]]
 name = "regex"
 version = "2024.11.6"
@@ -2647,4 +2680,4 @@ propcache = ">=0.2.0"
 [metadata]
 lock-version = "2.1"
 python-versions = "~=3.13"
-content-hash = 
"b0037bd47d793570a6513cf1b5d6303920af3aa7470c3a79525fb5ab1ad133d6"
+content-hash = 
"b8552c1164755503fcb5154d109acb0798abc7d5c1220a478ea3017d86be198f"
diff --git a/pyproject.toml b/pyproject.toml
index 95cb593..1161586 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,7 +19,8 @@ dependencies = [
   "hypercorn~=0.17",
   "python-gnupg~=0.5",
   "sqlmodel~=0.0",
-  "python-decouple~=3.8"
+  "python-decouple~=3.8",
+  "quart-schema~=0.21"
 ]
 
 [dependency-groups]
diff --git a/uv.lock b/uv.lock
index 2bfd4f6..6794dc9 100644
--- a/uv.lock
+++ b/uv.lock
@@ -867,6 +867,15 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl";,
 hash = 
"sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size 
= 1885186 },
 ]
 
+[[package]]
+name = "pyhumps"
+version = "3.8.0"
+source = { registry = "https://pypi.org/simple"; }
+sdist = { url = 
"https://files.pythonhosted.org/packages/c4/83/fa6f8fb7accb21f39e8f2b6a18f76f6d90626bdb0a5e5448e5cc9b8ab014/pyhumps-3.8.0.tar.gz";,
 hash = 
"sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3", size 
= 9018 }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/9e/11/a1938340ecb32d71e47ad4914843775011e6e9da59ba1229f181fef3119e/pyhumps-3.8.0-py3-none-any.whl";,
 hash = 
"sha256:060e1954d9069f428232a1adda165db0b9d8dfdce1d265d36df7fbff540acfd6", size 
= 6095 },
+]
+
 [[package]]
 name = "pyright"
 version = "1.1.394"
@@ -988,6 +997,19 @@ wheels = [
     { url = 
"https://files.pythonhosted.org/packages/7e/e9/cc28f21f52913adf333f653b9e0a3bf9cb223f5083a26422968ba73edd8d/quart-0.20.0-py3-none-any.whl";,
 hash = 
"sha256:003c08f551746710acb757de49d9b768986fd431517d0eb127380b656b98b8f1", size 
= 77960 },
 ]
 
+[[package]]
+name = "quart-schema"
+version = "0.21.0"
+source = { registry = "https://pypi.org/simple"; }
+dependencies = [
+    { name = "pyhumps" },
+    { name = "quart" },
+]
+sdist = { url = 
"https://files.pythonhosted.org/packages/36/d6/6ba58252b660cd2ff98f0758e46ce5aa3f39976c2db61dfcfa80883c570a/quart_schema-0.21.0.tar.gz";,
 hash = 
"sha256:5edcd18adb223c9d0f234688238882884fb09a4ee52fb29b50928821adbb7064", size 
= 23733 }
+wheels = [
+    { url = 
"https://files.pythonhosted.org/packages/ae/b7/c5cfa45ec6835005f72c7d667508e91a5f00f656ece2bd83e9ecb0d8643d/quart_schema-0.21.0-py3-none-any.whl";,
 hash = 
"sha256:57b8f7d2f8cc5bcbf6b2200ed17f870fc26811c5d0ac5c6a02352644ab068129", size 
= 21360 },
+]
+
 [[package]]
 name = "regex"
 version = "2024.11.6"
@@ -1118,6 +1140,7 @@ dependencies = [
     { name = "hypercorn" },
     { name = "python-decouple" },
     { name = "python-gnupg" },
+    { name = "quart-schema" },
     { name = "sqlmodel" },
 ]
 
@@ -1143,6 +1166,7 @@ requires-dist = [
     { name = "hypercorn", specifier = "~=0.17" },
     { name = "python-decouple", specifier = "~=3.8" },
     { name = "python-gnupg", specifier = "~=0.5" },
+    { name = "quart-schema", specifier = "~=0.21" },
     { name = "sqlmodel", specifier = "~=0.0" },
 ]
 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tooling.apache.org
For additional commands, e-mail: dev-h...@tooling.apache.org

Reply via email to