sts-sponsors team mailing list archive
-
sts-sponsors team
-
Mailing list archive
-
Message #08158
[Merge] ~ack/maas-site-manager:pending-sites-endpoint into maas-site-manager:main
Alberto Donato has proposed merging ~ack/maas-site-manager:pending-sites-endpoint into maas-site-manager:main.
Commit message:
add /requests endpoint for listing pending sites
Requested reviews:
MAAS Committers (maas-committers)
For more details, see:
https://code.launchpad.net/~ack/maas-site-manager/+git/site-manager/+merge/442427
--
Your team MAAS Committers is requested to review the proposed merge of ~ack/maas-site-manager:pending-sites-endpoint into maas-site-manager:main.
diff --git a/backend/msm/db/models.py b/backend/msm/db/models.py
index a04bc3e..ec3d032 100644
--- a/backend/msm/db/models.py
+++ b/backend/msm/db/models.py
@@ -38,6 +38,15 @@ class Site(BaseModel):
stats: SiteData | None
+class PendingSite(BaseModel):
+ """A pending MAAS site."""
+
+ id: int
+ name: str
+ url: str
+ created: datetime
+
+
class Token(BaseModel):
"""A registration token for a site."""
diff --git a/backend/msm/db/queries.py b/backend/msm/db/queries.py
index 9248559..3e51fbb 100644
--- a/backend/msm/db/queries.py
+++ b/backend/msm/db/queries.py
@@ -28,6 +28,7 @@ from ._tables import (
User,
)
from .models import (
+ PendingSite as PendingSiteSchema,
Site as SiteSchema,
Token as TokenSchema,
UserWithPassword as UserWithPasswordSchema,
@@ -97,7 +98,7 @@ def filters_from_arguments(
]
-async def get_filtered_sites(
+async def get_sites(
session: AsyncSession,
offset: int = 0,
limit: int = MAX_PAGE_SIZE,
@@ -109,7 +110,6 @@ async def get_filtered_sites(
street: list[str] | None = None,
timezone: list[str] | None = None,
url: list[str] | None = None,
- accepted: bool | None = None,
) -> tuple[int, Iterable[SiteSchema]]:
filters = filters_from_arguments(
Site,
@@ -122,8 +122,7 @@ async def get_filtered_sites(
timezone=timezone,
url=url,
)
- if accepted is not None:
- filters.append(Site.c.accepted == accepted)
+ filters.append(Site.c.accepted == True) # noqa
count = (
await session.execute(
select(func.count())
@@ -175,6 +174,34 @@ async def get_filtered_sites(
return count, [SiteSchema(**row._asdict()) for row in result.all()]
+async def get_pending_sites(
+ session: AsyncSession,
+ offset: int = 0,
+ limit: int = MAX_PAGE_SIZE,
+) -> tuple[int, Iterable[PendingSiteSchema]]:
+ filters = [Site.c.accepted == False] # noqa
+ count = (
+ await session.execute(
+ select(func.count()).select_from(Site).where(*filters)
+ )
+ ).scalar() or 0
+ stmt = (
+ select(
+ Site.c.id,
+ Site.c.name,
+ Site.c.url,
+ Site.c.created,
+ )
+ .select_from(Site)
+ .where(*filters)
+ .order_by(Site.c.id)
+ .limit(limit)
+ .offset(offset)
+ )
+ result = await session.execute(stmt)
+ return count, [PendingSiteSchema(**row._asdict()) for row in result.all()]
+
+
async def get_tokens(
session: AsyncSession,
offset: int = 0,
diff --git a/backend/msm/user_api/_handlers.py b/backend/msm/user_api/_handlers.py
index c1dcd0a..7d0bfac 100644
--- a/backend/msm/user_api/_handlers.py
+++ b/backend/msm/user_api/_handlers.py
@@ -32,6 +32,7 @@ from ._schema import (
CreateTokensRequest,
CreateTokensResponse,
JSONWebToken,
+ PaginatedPendingSites,
PaginatedSites,
PaginatedTokens,
UserLoginRequest,
@@ -49,12 +50,11 @@ async def sites(
pagination_params: PaginationParams = Depends(pagination_params),
filter_params: SiteFilterParams = Depends(site_filter_parameters),
) -> PaginatedSites:
- """Return all sites."""
- total, results = await queries.get_filtered_sites(
+ """Return accepted sites."""
+ total, results = await queries.get_sites(
session,
offset=pagination_params.offset,
limit=pagination_params.size,
- accepted=True, # only list accepted sites here
**filter_params._asdict(),
)
return PaginatedSites(
@@ -65,6 +65,25 @@ async def sites(
)
+async def pending_sites(
+ authenticated_user: Annotated[User, Depends(get_authenticated_user)],
+ session: AsyncSession = Depends(db_session),
+ pagination_params: PaginationParams = Depends(pagination_params),
+) -> PaginatedPendingSites:
+ """Return pending sites."""
+ total, results = await queries.get_pending_sites(
+ session,
+ offset=pagination_params.offset,
+ limit=pagination_params.size,
+ )
+ return PaginatedPendingSites(
+ total=total,
+ page=pagination_params.page,
+ size=pagination_params.size,
+ items=list(results),
+ )
+
+
async def tokens(
authenticated_user: Annotated[User, Depends(get_authenticated_user)],
session: AsyncSession = Depends(db_session),
diff --git a/backend/msm/user_api/_schema.py b/backend/msm/user_api/_schema.py
index ee6c3f8..af1da4f 100644
--- a/backend/msm/user_api/_schema.py
+++ b/backend/msm/user_api/_schema.py
@@ -7,6 +7,7 @@ from uuid import UUID
from pydantic import BaseModel
from ..db.models import (
+ PendingSite,
Site,
Token,
)
@@ -34,6 +35,10 @@ class PaginatedSites(PaginatedResults):
items: list[Site]
+class PaginatedPendingSites(PaginatedResults):
+ items: list[PendingSite]
+
+
class PaginatedTokens(PaginatedResults):
items: list[Token]
diff --git a/backend/msm/user_api/_setup.py b/backend/msm/user_api/_setup.py
index 6efd588..d9e976d 100644
--- a/backend/msm/user_api/_setup.py
+++ b/backend/msm/user_api/_setup.py
@@ -37,15 +37,18 @@ def create_app(db_dsn: str | None = None) -> FastAPI:
)
app.state.db = db
app.router.add_api_route("/", _handlers.root, methods=["GET"])
+ app.router.add_api_route(
+ "/login", _handlers.login_for_access_token, methods=["POST"]
+ )
+ app.router.add_api_route(
+ "/requests", _handlers.pending_sites, methods=["GET"]
+ )
app.router.add_api_route("/sites", _handlers.sites, methods=["GET"])
app.router.add_api_route("/tokens", _handlers.tokens, methods=["GET"])
app.router.add_api_route(
"/tokens", _handlers.tokens_post, methods=["POST"]
)
app.router.add_api_route(
- "/login", _handlers.login_for_access_token, methods=["POST"]
- )
- app.router.add_api_route(
"/users/me", _handlers.read_users_me, methods=["GET"]
)
return app
diff --git a/backend/tests/user_api/test_handlers.py b/backend/tests/user_api/test_handlers.py
index 8a5d97c..a1301f0 100644
--- a/backend/tests/user_api/test_handlers.py
+++ b/backend/tests/user_api/test_handlers.py
@@ -244,6 +244,52 @@ async def test_list_sites_with_stats(
@pytest.mark.asyncio
+async def test_list_pending_sites(
+ authenticated_user_app_client: AuthAsyncClient, fixture: Fixture
+) -> None:
+ site1 = {
+ "name": "LondonHQ",
+ "city": "London",
+ "country": "gb",
+ "latitude": "51.509865",
+ "longitude": "-0.118092",
+ "note": "the first site",
+ "region": "Blue Fin Bldg",
+ "street": "110 Southwark St",
+ "timezone": "Europe/London",
+ "url": "https://londoncalling.example.com",
+ "accepted": True,
+ }
+ site2 = site1.copy()
+ site2.update(
+ {
+ "name": "BerlinHQ",
+ "timezone": "Europe/Berlin",
+ "city": "Berlin",
+ "country": "de",
+ "accepted": False,
+ }
+ )
+ _, pending_site = await fixture.create("site", [site1, site2])
+
+ response = await authenticated_user_app_client.get("/requests")
+ assert response.status_code == 200
+ assert response.json() == {
+ "page": 1,
+ "size": 20,
+ "total": 1,
+ "items": [
+ {
+ "id": pending_site["id"],
+ "name": pending_site["name"],
+ "url": pending_site["url"],
+ "created": pending_site["created"].isoformat(),
+ },
+ ],
+ }
+
+
+@pytest.mark.asyncio
@pytest.mark.parametrize("time_format", ["ISO 8601", "Float"])
async def test_token_time_format(
time_format: str, authenticated_user_app_client: AuthAsyncClient
Follow ups