← Back to team overview

sts-sponsors team mailing list archive

[Merge] maas-site-manager:token-implementations into maas-site-manager:main

 

Jack Lloyd-Walters has proposed merging maas-site-manager:token-implementations into maas-site-manager:main.

Commit message:
token implementation changes

Requested reviews:
  MAAS Committers (maas-committers)

For more details, see:
https://code.launchpad.net/~maas-committers/maas-site-manager/+git/site-manager/+merge/440330

add test for iso 8601 duration string (extendable to other supported formats in future)
add token field "created", modify token field "expiration" to "expired"
-- 
Your team MAAS Committers is requested to review the proposed merge of maas-site-manager:token-implementations into maas-site-manager:main.
diff --git a/Makefile b/Makefile
index e73f7f8..ad5452d 100644
--- a/Makefile
+++ b/Makefile
@@ -61,6 +61,10 @@ ci-backend-test:
 	env -C backend tox -e test -- --junit-xml=../junit-backend.xml
 .PHONY: ci-test
 
+ci-backend-format:
+	env -C backend tox -e format
+.PHONY: ci-backend-format
+
 
 # Frontend CI targets
 
diff --git a/backend/msm/db/_tables.py b/backend/msm/db/_tables.py
index 649a58b..14dddc4 100644
--- a/backend/msm/db/_tables.py
+++ b/backend/msm/db/_tables.py
@@ -56,7 +56,8 @@ Token = Table(
     Column(
         "value", UUID(as_uuid=True), nullable=False, index=True, default=uuid4
     ),
-    Column("expiration", DateTime, nullable=False),
+    Column("expired", DateTime, nullable=False),
+    Column("created", DateTime, nullable=False),
 )
 
 
diff --git a/backend/msm/db/queries.py b/backend/msm/db/queries.py
index 3aa13d4..eba2e60 100644
--- a/backend/msm/db/queries.py
+++ b/backend/msm/db/queries.py
@@ -154,7 +154,8 @@ async def get_tokens(
             Token.c.id,
             Token.c.site_id,
             Token.c.value,
-            Token.c.expiration,
+            Token.c.expired,
+            Token.c.created,
         )
         .select_from(Token)
         .offset(offset)
@@ -166,14 +167,16 @@ async def get_tokens(
 async def create_tokens(
     session: AsyncSession, duration: timedelta, count: int = 1
 ) -> tuple[datetime, list[UUID]]:
-    expiration = datetime.utcnow() + duration
+    created = datetime.utcnow()
+    expired = datetime.utcnow() + duration
     result = await session.execute(
         Token.insert().returning(Token.c.value),
         [
             {
-                "expiration": expiration,
+                "expired": expired,
+                "created": created,
             }
             for _ in range(count)
         ],
     )
-    return expiration, [row[0] for row in result]
+    return expired, [row[0] for row in result]
diff --git a/backend/msm/schema.py b/backend/msm/schema.py
index 96312b2..d57933f 100644
--- a/backend/msm/schema.py
+++ b/backend/msm/schema.py
@@ -112,7 +112,8 @@ class CreateToken(BaseModel):
 
     site_id: int | None
     value: UUID
-    expiration: datetime
+    expired: datetime
+    created: datetime
 
 
 class Token(CreateToken):
@@ -140,5 +141,5 @@ class CreateTokensRequest(BaseModel):
 class CreateTokensResponse(BaseModel):
     """List of created tokens, along with their duration."""
 
-    expiration: datetime
+    expired: datetime
     tokens: list[UUID]
diff --git a/backend/msm/user_api/_base.py b/backend/msm/user_api/_base.py
index 8a56be1..a3797a2 100644
--- a/backend/msm/user_api/_base.py
+++ b/backend/msm/user_api/_base.py
@@ -85,9 +85,9 @@ async def tokens_post(
     Create one or more tokens.
     Token duration (TTL) is expressed in seconds.
     """
-    expiration, tokens = await queries.create_tokens(
+    expired, tokens = await queries.create_tokens(
         session,
         create_request.duration,
         count=create_request.count,
     )
-    return schema.CreateTokensResponse(expiration=expiration, tokens=tokens)
+    return schema.CreateTokensResponse(expired=expired, tokens=tokens)
diff --git a/backend/msm/user_api/tests/test_handlers.py b/backend/msm/user_api/tests/test_handlers.py
index 0784840..c7a35ab 100644
--- a/backend/msm/user_api/tests/test_handlers.py
+++ b/backend/msm/user_api/tests/test_handlers.py
@@ -2,6 +2,8 @@ from datetime import (
     datetime,
     timedelta,
 )
+from typing import Any
+from uuid import uuid4
 
 # from fastapi.testclient import TestClient
 from httpx import AsyncClient
@@ -10,6 +12,29 @@ import pytest
 from ...testing.db import Fixture
 
 
+def datetime_format(time: datetime, time_format: str) -> str:
+    # check if the format matches any pre-defined forms
+    match time_format.lower().split()[0]:
+        case "ctime":
+            return time.ctime()
+        case "float":
+            return time.timestamp()
+        case "iso":
+            return time.isoformat()
+        case "ordinal":
+            return time.toordinal()
+    # Otherwise, attempr to parse with the format
+    return time.strftime(time_format)
+
+def duration_format(time: timedelta, time_format: str) -> str:
+    # check if the format matches any pre-defined forms
+    match time_format.lower().split()[0]:
+        case "float":
+            return str(int(time.total_seconds()))
+        case "iso":
+            d, s = time.days, time.seconds
+            return f"P{d//365}Y0M{d%365}DT{s//60//60}H{s//60%60}M{s%60}S"
+
 @pytest.mark.asyncio
 async def test_root(user_app_client: AsyncClient) -> None:
     response = await user_app_client.get("/")
@@ -68,15 +93,18 @@ async def test_list_sites(
 
 
 @pytest.mark.asyncio
-async def test_create_token(user_app_client: AsyncClient) -> None:
-    seconds = 100
+@pytest.mark.parametrize("time_format", ["ISO 8601", "Float"])
+async def test_token_time_format(time_format: str, user_app_client: AsyncClient) -> None:
+    expiry = timedelta(seconds=100)
+    formatted_expiry = duration_format(expiry, time_format)
+
     response = await user_app_client.post(
-        "/tokens", json={"count": 5, "duration": seconds}
+        "/tokens", json={"count": 5, "duration": formatted_expiry}
     )
     assert response.status_code == 200
     result = response.json()
-    assert datetime.fromisoformat(result["expiration"]) < (
-        datetime.utcnow() + timedelta(seconds=seconds)
+    assert datetime.fromisoformat(result["expired"]) < (
+        datetime.utcnow() + timedelta(seconds=formatted_expiry)
     )
     assert len(result["tokens"]) == 5
 
@@ -90,13 +118,15 @@ async def test_list_tokens(
             "id": 1,
             "site_id": None,
             "value": "c54e5ba6-d214-40dd-b601-01ebb1019c07",
-            "expiration": datetime.fromisoformat("2023-02-23T09:09:51.103703"),
+            "expired": datetime.fromisoformat("2023-02-23T09:09:51.103703"),
+            "created": datetime.fromisoformat("2023-02-22T03:14:15.926535")
         },
         {
             "id": 2,
             "site_id": None,
             "value": "b67c449e-fcf6-4014-887d-909859f9fb70",
-            "expiration": datetime.fromisoformat("2023-02-23T11:28:54.382456"),
+            "expired": datetime.fromisoformat("2023-02-23T11:28:54.382456"),
+            "created": datetime.fromisoformat("2023-02-22T03:14:15.926535")
         },
     ]
     await fixture.create("tokens", tokens)
diff --git a/test-data/import.sql b/test-data/import.sql
index a7198af..9c3ec1d 100644
--- a/test-data/import.sql
+++ b/test-data/import.sql
@@ -4,7 +4,7 @@ DELIMITER ','
 QUOTE '"'
 CSV HEADER;
 
-COPY tokens(site_id, value, expiration)
+COPY tokens(site_id, value, expired, created)
 FROM '/tokens.csv'
 DELIMITER ','
 QUOTE '"'
diff --git a/test-data/tokens.csv b/test-data/tokens.csv
index 0a4848b..c77e2a2 100644
--- a/test-data/tokens.csv
+++ b/test-data/tokens.csv
@@ -1,12 +1,12 @@
-site_id,value,expiration
-1,c96abba0-9962-41e5-9f40-6e2b55578c06,2023-03-01 00:00:00
-2,8960d8a1-7c5e-4331-8f58-591cf145a313,2023-03-01 00:00:00
-3,dffa13fe-d2c5-43b4-81e1-1efde6eeec76,2023-03-01 00:00:00
-4,a31a7408-09ee-4403-98c1-5c59ca3534c2,2023-03-01 00:00:00
-5,4d015359-3093-4216-ac87-6dfa29d0d6c5,2023-03-01 00:00:00
-6,9f404751-a17b-456c-8fa9-46b7258cb471,2023-03-01 00:00:00
-7,269773b7-b47d-46e8-9825-eb25eda99771,2023-03-01 00:00:00
-8,fce21bd4-3a92-42af-adb4-6b46dc0aefd4,2023-03-01 00:00:00
-,0e846493-fde9-4d15-844c-2ca0341d1e84,2024-01-01 00:00:00
-,e15a7d3c-9df8-40c7-b81b-ed4796e777bc,2024-01-01 00:00:00
-,87a62d9a-7645-43b5-9dd4-eaf53e768c4a,2024-01-01 00:00:00
+site_id,value,expired,created
+1,c96abba0-9962-41e5-9f40-6e2b55578c06,2023-03-01 00:00:00,2023-02-28 00:00:00
+2,8960d8a1-7c5e-4331-8f58-591cf145a313,2023-03-01 00:00:00,2023-02-28 00:00:00
+3,dffa13fe-d2c5-43b4-81e1-1efde6eeec76,2023-03-01 00:00:00,2023-02-28 00:00:00
+4,a31a7408-09ee-4403-98c1-5c59ca3534c2,2023-03-01 00:00:00,2023-02-28 00:00:00
+5,4d015359-3093-4216-ac87-6dfa29d0d6c5,2023-03-01 00:00:00,2023-02-28 00:00:00
+6,9f404751-a17b-456c-8fa9-46b7258cb471,2023-03-01 00:00:00,2023-02-28 00:00:00
+7,269773b7-b47d-46e8-9825-eb25eda99771,2023-03-01 00:00:00,2023-02-28 00:00:00
+8,fce21bd4-3a92-42af-adb4-6b46dc0aefd4,2023-03-01 00:00:00,2023-02-28 00:00:00
+,0e846493-fde9-4d15-844c-2ca0341d1e84,2024-01-01 00:00:00,2023-02-28 00:00:00
+,e15a7d3c-9df8-40c7-b81b-ed4796e777bc,2024-01-01 00:00:00,2023-02-28 00:00:00
+,87a62d9a-7645-43b5-9dd4-eaf53e768c4a,2024-01-01 00:00:00,2023-02-28 00:00:00

Follow ups