Skip to content

Commit a67375e

Browse files
committed
Polishing schemas endpoint only including the get_all_schemas method
Clean up SchemaDefinition Added condition for uniqueness of title in schemas_collection
1 parent 2bfd6e1 commit a67375e

File tree

5 files changed

+98
-101
lines changed

5 files changed

+98
-101
lines changed

jsoned/api/api_v1/api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
# TODO add login with oauth2
21
from fastapi import APIRouter
32

43
from jsoned.api.api_v1.endpoints import schemas
54

65
api_router = APIRouter()
76
api_router.include_router(schemas.router, prefix="/schemas", tags=["schemas"])
7+
# TODO add login with oauth2
8+
# api_router.include_router(login.router, prefix="/login", tags=["login"])
Lines changed: 75 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
from datetime import datetime
2-
from typing import Any
3-
from uuid import uuid4
4-
5-
from fastapi import APIRouter, HTTPException
6-
from fastapi.encoders import jsonable_encoder
1+
from fastapi import APIRouter
72

83
from jsoned.database import schemas_collection
94
from jsoned.models.schema_definition import SchemaDefinition
@@ -12,89 +7,79 @@
127

138

149
@router.get("/all", response_model=list[SchemaDefinition])
15-
async def get_all_schemas() -> list[dict[str, Any]]:
16-
"""
17-
Retrieve all schemas. Ensures each document has `id` as a string and a valid `updated_at`.
18-
"""
19-
docs = list(schemas_collection.find())
20-
normalized: list[dict[str, Any]] = []
21-
22-
for d in docs:
23-
# Ensure `id` exists and is a non-empty string
24-
if not isinstance(d.get("id"), str) or not d["id"].strip():
25-
d["id"] = str(uuid4())
26-
27-
# Ensure `updated_at` is present (optional safeguard)
28-
if d.get("updated_at") is None:
29-
d["updated_at"] = datetime.utcnow()
30-
31-
normalized.append(d)
32-
33-
# No BSON present; plain JSON encoding is fine
34-
return jsonable_encoder(normalized)
35-
36-
37-
@router.post("/add", response_model=SchemaDefinition)
38-
async def add_schema(schema: SchemaDefinition) -> dict[str, Any]:
10+
async def get_all_schemas():
3911
"""
40-
Add a new schema. If `id` is missing/empty, generate one; always refresh `updated_at`.
12+
Gets all the schemas present in the database and returns them in a list of `SchemaDefinition` models.
4113
"""
42-
doc = schema.dict()
43-
44-
# Guarantee a usable id
45-
if not isinstance(doc.get("id"), str) or not doc["id"].strip():
46-
doc["id"] = str(uuid4())
47-
48-
# Server-side timestamp
49-
doc["updated_at"] = datetime.utcnow()
50-
51-
# Insert as-is (no ObjectId conversions)
52-
schemas_collection.insert_one(doc)
53-
54-
# Return exactly what we stored
55-
return jsonable_encoder(doc)
56-
57-
58-
@router.put("/update/{id}", response_model=dict[str, str])
59-
async def update_schema(id: str, update: SchemaDefinition) -> dict[str, str]:
60-
"""
61-
Update schema by `id`. Ignores `id` field in the payload (primary key is immutable).
62-
Only non-None fields are updated; `updated_at` is refreshed automatically.
63-
"""
64-
if not isinstance(id, str) or not id.strip():
65-
raise HTTPException(
66-
status_code=400, detail="Invalid schema id (must be a non-empty string)"
67-
)
68-
69-
# Ignore None values and prevent changing the primary key
70-
update_fields = {
71-
k: v for k, v in update.dict().items() if v is not None and k != "id"
72-
}
73-
74-
if update_fields:
75-
update_fields["updated_at"] = datetime.utcnow()
76-
77-
result = schemas_collection.update_one(
78-
{"id": id}, {"$set": update_fields} if update_fields else {}
79-
)
80-
if result.matched_count == 0:
81-
raise HTTPException(status_code=404, detail="Schema not found")
82-
83-
return {"message": "Schema updated"}
84-
85-
86-
@router.delete("/delete/{id}", response_model=dict[str, str])
87-
async def delete_schema(id: str) -> dict[str, str]:
88-
"""
89-
Delete schema by `id`.
90-
"""
91-
if not isinstance(id, str) or not id.strip():
92-
raise HTTPException(
93-
status_code=400, detail="Invalid schema id (must be a non-empty string)"
94-
)
95-
96-
result = schemas_collection.delete_one({"id": id})
97-
if result.deleted_count == 0:
98-
raise HTTPException(status_code=404, detail="Schema not found")
99-
100-
return {"message": "Schema deleted"}
14+
response = []
15+
documents = schemas_collection.find({})
16+
async for doc in documents:
17+
response.append(SchemaDefinition(**doc))
18+
19+
return response
20+
21+
22+
# @router.post("/add", response_model=SchemaDefinition)
23+
# async def add_schema(schema: SchemaDefinition) -> dict[str, Any]:
24+
# """
25+
# Add a new schema. If `id` is missing/empty, generate one; always refresh `updated_at`.
26+
# """
27+
# doc = schema.dict()
28+
29+
# # Guarantee a usable id
30+
# if not isinstance(doc.get("id"), str) or not doc["id"].strip():
31+
# doc["id"] = str(uuid4())
32+
33+
# # Server-side timestamp
34+
# doc["updated_at"] = datetime.utcnow()
35+
36+
# # Insert as-is (no ObjectId conversions)
37+
# schemas_collection.insert_one(doc)
38+
39+
# # Return exactly what we stored
40+
# return jsonable_encoder(doc)
41+
42+
43+
# @router.put("/update/{id}", response_model=dict[str, str])
44+
# async def update_schema(id: str, update: SchemaDefinition) -> dict[str, str]:
45+
# """
46+
# Update schema by `id`. Ignores `id` field in the payload (primary key is immutable).
47+
# Only non-None fields are updated; `updated_at` is refreshed automatically.
48+
# """
49+
# if not isinstance(id, str) or not id.strip():
50+
# raise HTTPException(
51+
# status_code=400, detail="Invalid schema id (must be a non-empty string)"
52+
# )
53+
54+
# # Ignore None values and prevent changing the primary key
55+
# update_fields = {
56+
# k: v for k, v in update.dict().items() if v is not None and k != "id"
57+
# }
58+
59+
# if update_fields:
60+
# update_fields["updated_at"] = datetime.utcnow()
61+
62+
# result = schemas_collection.update_one(
63+
# {"id": id}, {"$set": update_fields} if update_fields else {}
64+
# )
65+
# if result.matched_count == 0:
66+
# raise HTTPException(status_code=404, detail="Schema not found")
67+
68+
# return {"message": "Schema updated"}
69+
70+
71+
# @router.delete("/delete/{id}", response_model=dict[str, str])
72+
# async def delete_schema(id: str) -> dict[str, str]:
73+
# """
74+
# Delete schema by `id`.
75+
# """
76+
# if not isinstance(id, str) or not id.strip():
77+
# raise HTTPException(
78+
# status_code=400, detail="Invalid schema id (must be a non-empty string)"
79+
# )
80+
81+
# result = schemas_collection.delete_one({"id": id})
82+
# if result.deleted_count == 0:
83+
# raise HTTPException(status_code=404, detail="Schema not found")
84+
85+
# return {"message": "Schema deleted"}

jsoned/database.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@
1111
client = AsyncIOMotorClient("mongodb://localhost:27017")
1212
database = client.jsoned_db
1313
schemas_collection = database.schemas
14+
15+
# Ensure that the `title` field is unique
16+
schemas_collection.create_index([("title", 1)], unique=True)

jsoned/models/schema_definition.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
from datetime import datetime
2+
from uuid import UUID
23

34
from pydantic import BaseModel, Field
45

56

67
class SchemaDefinition(BaseModel):
7-
id: str = Field(..., description="Unique identifier for the schema")
8-
name: str | None = Field(
8+
id: UUID = Field(
9+
...,
10+
description="Automatically generated unique identifier for the schema entry based on `content`.",
11+
)
12+
13+
title: str | None = Field(
914
None,
10-
description="Human-readable name of the schema",
15+
description="A human-readable title given to the schema entry.",
1116
min_length=3,
1217
)
13-
version: str | None = Field(None, description="Version of the schema")
14-
content: dict | None = Field(
15-
None, description="The actual schema content as a dictionary"
16-
)
18+
1719
updated_at: datetime | None = Field(
18-
None, description="Timestamp of the last update"
20+
None,
21+
description="Timestamp of the last update of the schema entry.",
22+
)
23+
24+
content: dict | None = Field(
25+
None,
26+
description="The actual schema content as a dictionary",
1927
)

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ dev = [
4848

4949
[tool.ruff]
5050
# Exclude a variety of commonly ignored directories.
51-
include = ["backend/*.py", "tests/*.py"]
51+
include = ["jsoned/*.py", "tests/*.py"]
5252
exclude = [
5353
".bzr",
5454
".direnv",
@@ -128,5 +128,5 @@ where = ["."]
128128
namespaces = false
129129

130130
[tool.setuptools_scm]
131-
write_to = "backend/_version.py"
131+
write_to = "jsoned/_version.py"
132132

0 commit comments

Comments
 (0)