Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion backend/app/models/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class MetadataDefinitionBase(BaseModel):

name: str
description: Optional[str]
created: datetime = Field(default_factory=datetime.utcnow)
context: Optional[
List[Union[dict, AnyUrl]]
] # https://json-ld.org/spec/latest/json-ld/#the-context
Expand Down Expand Up @@ -121,7 +122,8 @@ class Settings:


class MetadataDefinitionOut(MetadataDefinitionDB):
pass
class Config:
fields = {"id": "id"}


def validate_definition(content: dict, metadata_def: MetadataDefinitionOut):
Expand Down
3 changes: 1 addition & 2 deletions backend/app/routers/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from beanie.operators import Or, Push, RegEx
from bson.objectid import ObjectId
from fastapi import HTTPException, Depends, APIRouter
from pymongo import DESCENDING

from app.deps.authorization_deps import AuthorizationDB, GroupAuthorization
from app.keycloak_auth import get_current_user, get_user
Expand Down Expand Up @@ -38,7 +37,7 @@ async def get_groups(
"""Get a list of all Groups in the db the user is a member/owner of.

Arguments:
skip -- number of initial records to skip (i.e. for pagination)
skip -- number of initial recoto_list()rds to skip (i.e. for pagination)
limit -- restrict number of records to be returned (i.e. for pagination)


Expand Down
103 changes: 95 additions & 8 deletions backend/app/routers/metadata.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from typing import Optional, List

from beanie import PydanticObjectId
from beanie.odm.operators.find.evaluation import RegEx
from beanie.odm.operators.find.logical import Or
from elasticsearch import Elasticsearch
from fastapi import (
APIRouter,
Expand Down Expand Up @@ -44,24 +47,108 @@ async def save_metadata_definition(


@router.get("/definition", response_model=List[MetadataDefinitionOut])
async def get_metadata_definition(
async def get_metadata_definition_list(
name: Optional[str] = None,
user=Depends(get_current_user),
skip: int = 0,
limit: int = 2,
):
if name is None:
defs = await MetadataDefinitionDB.find().skip(skip).limit(limit).to_list()
defs = await MetadataDefinitionDB.find(
sort=(-MetadataDefinitionDB.created), skip=skip, limit=limit
).to_list()
else:
defs = (
await MetadataDefinitionDB.find(MetadataDefinitionDB.name == name)
.skip(skip)
.limit(limit)
.to_list()
)
defs = await MetadataDefinitionDB.find(
MetadataDefinitionDB.name == name,
sort=(-MetadataDefinitionDB.created),
skip=skip,
limit=limit,
).to_list()
return [mddef.dict() for mddef in defs]


@router.get(
"/definition/{metadata_definition_id}", response_model=MetadataDefinitionOut
)
async def get_metadata_definition(
metadata_definition_id: str,
user=Depends(get_current_user),
):
if (
mdd := await MetadataDefinitionDB.get(PydanticObjectId(metadata_definition_id))
) is not None:
return mdd.dict()
raise HTTPException(
status_code=404,
detail=f"Metadata definition {metadata_definition_id} not found",
)


@router.delete(
"/definition/{metadata_definition_id}", response_model=MetadataDefinitionOut
)
async def delete_metadata_definition(
metadata_definition_id: str,
user=Depends(get_current_user),
):
"""Delete metadata definition by specific ID."""
mdd = await MetadataDefinitionDB.find_one(
MetadataDefinitionDB.id == PyObjectId(metadata_definition_id)
)
if mdd:
# Check if metadata using this definition exists
metadata_using_definition = await MetadataDB.find(
MetadataDB.definition == mdd.name
).to_list()

if len(metadata_using_definition) > 0:
raise HTTPException(
status_code=400,
detail=f"Metadata definition: {mdd.name} ({metadata_definition_id}) in use. "
f"You cannot delete it until all metadata records using it are deleted.",
)

# TODO: Refactor this with permissions checks etc.
await mdd.delete()
return mdd.dict()
else:
raise HTTPException(
status_code=404,
detail=f"Metadata definition {metadata_definition_id} not found",
)


@router.get(
"/definition/search/{search_term}", response_model=List[MetadataDefinitionOut]
)
async def search_metadata_definition(
search_term: str,
skip: int = 0,
limit: int = 10,
user=Depends(get_current_user),
):
"""Search all metadata definition in the db based on text.

Arguments:
text -- any text matching name or description
skip -- number of initial records to skip (i.e. for pagination)
limit -- restrict number of records to be returned (i.e. for pagination)
"""

mdds = await MetadataDefinitionDB.find(
Or(
RegEx(field=MetadataDefinitionDB.name, pattern=search_term),
RegEx(field=MetadataDefinitionDB.description, pattern=search_term),
RegEx(field=MetadataDefinitionDB.context, pattern=search_term),
),
sort=(-MetadataDefinitionDB.created),
skip=skip,
limit=limit,
).to_list()

return [mdd.dict() for mdd in mdds]


@router.patch("/{metadata_id}", response_model=MetadataOut)
async def update_metadata(
metadata_in: MetadataPatch,
Expand Down
35 changes: 34 additions & 1 deletion backend/app/tests/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,32 @@ def test_dataset_create_metadata_no_context(client: TestClient, headers: dict):
assert response.status_code == 400


def test_dataset_create_metadata_definition(client: TestClient, headers: dict):
def test_dataset_create_and_delete_metadata_definition_success(
client: TestClient, headers: dict
):
# Post the definition itself
response = client.post(
f"{settings.API_V2_STR}/metadata/definition",
json=metadata_definition2,
headers=headers,
)
assert (
response.status_code == 200 or response.status_code == 409
) # 409 = definition already exists
metadata_definition_id = response.json().get("id")

# Delete metadata definition
response = client.delete(
f"{settings.API_V2_STR}/metadata/definition/{metadata_definition_id}",
headers=headers,
)
assert response.status_code == 200
assert response.json().get("id") == metadata_definition_id


def test_dataset_create_and_delete_metadata_definition_fail(
client: TestClient, headers: dict
):
# Post the definition itself
response = client.post(
f"{settings.API_V2_STR}/metadata/definition",
Expand All @@ -84,6 +109,7 @@ def test_dataset_create_metadata_definition(client: TestClient, headers: dict):
assert (
response.status_code == 200 or response.status_code == 409
) # 409 = definition already exists
metadata_definition_id = response.json().get("id")

# check if @context is injected correctly
assert response.json().get("@context") is not None
Expand All @@ -108,6 +134,13 @@ def test_dataset_create_metadata_definition(client: TestClient, headers: dict):
)
assert response.status_code == 409

# Delete metadata definition
response = client.delete(
f"{settings.API_V2_STR}/metadata/definition/{metadata_definition_id}",
headers=headers,
)
assert response.status_code == 400


@pytest.mark.asyncio
async def test_dataset_patch_metadata_definition(client: TestClient, headers: dict):
Expand Down
Loading