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
39 changes: 37 additions & 2 deletions mds/src/fairscape_mds/crud/fairscape_request.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,48 @@
import re
from fairscape_mds.core.config import FairscapeConfig


def flexible_ark_query(guid: str):
"""Build a MongoDB query that matches an ARK with or without dashes
and with or without a slash after 'ark:'. Returns None if guid
doesn't look like an ARK. Matches ARK SPEC"""
ark_match = re.match(r'^ark:/?([\d]+)/(.*)', guid)
if not ark_match:
return None
naan = ark_match.group(1)
postfix = ark_match.group(2)
stripped = postfix.replace('-', '')
fuzzy_postfix = '-?'.join(re.escape(c) for c in stripped)
pattern = f'^ark:{naan}/{fuzzy_postfix}$'
return {"@id": {"$regex": pattern}}


class FairscapeRequest():
def __init__(
self,
self,
backendConfig: FairscapeConfig
):
self.config = backendConfig

def getMetadata(self, guid: str):
return self.config.identifierCollection.find_one({"@id": guid}, projection={"_id": False})

def flexibleFind(self, guid: str, projection=None):
"""Look up an identifier by exact match first, then fall back to
a dash-insensitive and ark:/ark: tolerant regex search."""
if projection is None:
projection = {"_id": False}
# Exact match
result = self.config.identifierCollection.find_one(
{"@id": guid}, projection=projection
)
if result:
return result
# Flexible fallback
query = flexible_ark_query(guid)
if query:
result = self.config.identifierCollection.find_one(
query, projection=projection
)
return result

24 changes: 6 additions & 18 deletions mds/src/fairscape_mds/crud/identifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,9 @@ class IdentifierRequest(FairscapeRequest):
def getIdentifier(self, guid):
""" Find Identifier metadata and marshal into a StoredIdentifier class
"""

# get the metadata for a stored identifier
datasetMetadata = self.config.identifierCollection.find_one(
{"@id": guid},
projection={"_id": False}
)
datasetMetadata = self.flexibleFind(guid)

if not datasetMetadata:
raise IdentifierNotFound(
Expand Down Expand Up @@ -170,10 +167,7 @@ def getContent(self, guid: str)->FairscapeResponse:
"""

# get the metadata
metadata = self.config.identifierCollection.find_one(
{"@id": guid},
projection={"_id": False}
)
metadata = self.flexibleFind(guid)

if not metadata:
return FairscapeResponse(
Expand Down Expand Up @@ -234,10 +228,7 @@ def updatePublicationStatus(
newStatus = publicationChange.publicationStatus

# get the identifier metadata
metadata = self.config.identifierCollection.find_one(
{"@id": guid},
projection={"_id": False}
)
metadata = self.flexibleFind(guid)

if not metadata:
return FairscapeResponse(
Expand All @@ -246,7 +237,7 @@ def updatePublicationStatus(
error={"error": "identifier not found"}
)

# serialize metadata into model
# serialize metadata into model
try:
foundIdentifier = StoredIdentifier.model_validate(metadata)
except ValidationError as e:
Expand Down Expand Up @@ -357,10 +348,7 @@ def updateMetadata(
"""

# check if identifier exists
foundMetadata = self.config.identifierCollection.find_one(
{"@id": guid},
projection={"_id": False}
)
foundMetadata = self.flexibleFind(guid)

if not foundMetadata:
return FairscapeResponse(
Expand Down
7 changes: 2 additions & 5 deletions mds/src/fairscape_mds/crud/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@

class FairscapeResolverRequest(FairscapeRequest):

def resolveIdentifier(self, guid: str):
foundMetadata = self.config.identifierCollection.find_one(
{"@id": guid},
projection={"_id": False}
)
def resolveIdentifier(self, guid: str):
foundMetadata = self.flexibleFind(guid)

if not foundMetadata:
return FairscapeResponse(
Expand Down
21 changes: 5 additions & 16 deletions mds/src/fairscape_mds/crud/rocrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -1037,10 +1037,7 @@ def _build_rocrate_structure(self, root_guid: str, root_metadata: dict, parts: l
}

def getROCrateMetadata(self, rocrateGUID: str):
root_doc = self.config.identifierCollection.find_one(
{"@id": rocrateGUID},
projection={"_id": False}
)
root_doc = self.flexibleFind(rocrateGUID)

if not root_doc:
return FairscapeResponse(
Expand Down Expand Up @@ -1084,12 +1081,7 @@ def getROCrateMetadata(self, rocrateGUID: str):


def getROCrateMetadataElem(self, rocrateGUID: str):
rocrateMetadata = self.config.identifierCollection.find_one(
{
"@id": rocrateGUID
},
projection={"_id": False}
)
rocrateMetadata = self.flexibleFind(rocrateGUID)

# if no metadata is found return 404
if not rocrateMetadata:
Expand Down Expand Up @@ -1126,8 +1118,8 @@ def getROCrateContentSummary(
"""

# Only fetch the contentSummary field
rocrate_doc = self.config.identifierCollection.find_one(
{"@id": rocrateGUID},
rocrate_doc = self.flexibleFind(
rocrateGUID,
projection={
"_id": False,
"contentSummary": 1,
Expand Down Expand Up @@ -1222,10 +1214,7 @@ def downloadROCrateArchive(
rocrateGUID: str
):

rocrateIdentifier = self.config.identifierCollection.find_one(
{"@id": rocrateGUID},
projection={"_id": False}
)
rocrateIdentifier = self.flexibleFind(rocrateGUID)

# if no metadata is found return 404
if not rocrateIdentifier:
Expand Down
2 changes: 2 additions & 0 deletions mds/src/fairscape_mds/routers/computation.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def createComputation(
)


@computationRouter.get("/computation/ark:/{NAAN}/{postfix}")
@computationRouter.get("/computation/ark:{NAAN}/{postfix}")
def getComputationMetadata(
NAAN: str,
Expand All @@ -57,6 +58,7 @@ def getComputationMetadata(
)


@computationRouter.delete("/computation/ark:/{NAAN}/{postfix}")
@computationRouter.delete("/computation/ark:{NAAN}/{postfix}")
def deleteComputation(
NAAN: str,
Expand Down
1 change: 1 addition & 0 deletions mds/src/fairscape_mds/routers/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def getDatasetContent(
)


@datasetRouter.delete("/dataset/ark:/{NAAN}/{postfix}")
@datasetRouter.delete("/dataset/ark:{NAAN}/{postfix}")
def deleteDataset(
NAAN: str,
Expand Down
9 changes: 9 additions & 0 deletions mds/src/fairscape_mds/routers/evidence_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def create_evidence_graph_route(
else:
raise HTTPException(status_code=response.statusCode, detail=response.error)

@router.get("/ark:/{NAAN}/{postfix}", response_model=StoredIdentifier, summary="Get an EvidenceGraph by its ARK ID")
@router.get("/ark:{NAAN}/{postfix}", response_model=StoredIdentifier, summary="Get an EvidenceGraph by its ARK ID")
def get_evidence_graph_route(
NAAN: Annotated[str, Path(description="Name Assigning Authority Number of the ARK ID")],
Expand All @@ -46,6 +47,7 @@ def get_evidence_graph_route(
raise HTTPException(status_code=response.statusCode, detail=response.error)


@router.get("/query/ark:/{NAAN}/{postfix}", response_model=StoredIdentifier, summary="Get an EvidenceGraph by its ARK ID")
@router.get("/query/ark:{NAAN}/{postfix}", response_model=StoredIdentifier, summary="Get an EvidenceGraph by its ARK ID")
def get_evidence_graph_query_route(
NAAN: Annotated[str, Path(description="Name Assigning Authority Number of the ARK ID")],
Expand All @@ -60,6 +62,7 @@ def get_evidence_graph_query_route(
raise HTTPException(status_code=response.statusCode, detail=response.error)


@router.delete("/ark:/{NAAN}/{postfix}", summary="Delete an EvidenceGraph by its ARK ID")
@router.delete("/ark:{NAAN}/{postfix}", summary="Delete an EvidenceGraph by its ARK ID")
def delete_evidence_graph_route(
NAAN: Annotated[str, Path(description="Name Assigning Authority Number of the ARK ID")],
Expand All @@ -84,6 +87,12 @@ def list_evidence_graphs_route():
else:
raise HTTPException(status_code=response.statusCode, detail=response.error)

@router.post(
"/build/ark:/{NAAN}/{postfix}",
status_code=status.HTTP_202_ACCEPTED,
response_model=Dict,
summary="Initiate building or rebuilding the EvidenceGraph for a given node ARK ID"
)
@router.post(
"/build/ark:{NAAN}/{postfix}",
status_code=status.HTTP_202_ACCEPTED,
Expand Down
2 changes: 2 additions & 0 deletions mds/src/fairscape_mds/routers/publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def updatePublicationStatus(
)


@publishRouter.get(path="/view/ark:/{NAAN}/{postfix}")
@publishRouter.get(path="/view/ark:{NAAN}/{postfix}")
def viewContent(
NAAN: str,
Expand Down Expand Up @@ -77,6 +78,7 @@ def viewContent(
)


@publishRouter.get(path="/download/ark:/{NAAN}/{postfix}")
@publishRouter.get(path="/download/ark:{NAAN}/{postfix}")
def downloadContent(
NAAN: str,
Expand Down
10 changes: 10 additions & 0 deletions mds/src/fairscape_mds/routers/publish_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@

publish_request_handler = FairscapePublishRequest(appConfig)

@router.post(
"/create/ark:/{NAAN}/{postfix}",
summary="Create a dataset on a publishing platform for an ROCrate",
response_description="Details of the created dataset and ROCrate update status"
)
@router.post(
"/create/ark:{NAAN}/{postfix}",
summary="Create a dataset on a publishing platform for an ROCrate",
Expand Down Expand Up @@ -57,6 +62,11 @@ async def create_dataset_endpoint(

return JSONResponse(status_code=response.statusCode, content=response.model)

@router.post(
"/upload/ark:/{NAAN}/{postfix}",
summary="Upload ROCrate archive to an existing platform dataset",
response_description="Details of the file upload transaction."
)
@router.post(
"/upload/ark:{NAAN}/{postfix}",
summary="Upload ROCrate archive to an existing platform dataset",
Expand Down
11 changes: 11 additions & 0 deletions mds/src/fairscape_mds/routers/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ def resolveARK(
)


@resolverRouter.get("/ark:/{NAAN}/{postfix}")
def resolveARKWithSlash(
NAAN: str,
postfix: str,
accept: Optional[str] = Header(default="application/json")
):
return resolveARK(NAAN, postfix, accept)


@resolverRouter.put("/ark:/{NAAN}/{postfix}")
@resolverRouter.put("/ark:{NAAN}/{postfix}")
def updateARK(
currentUser: Annotated[UserWriteModel, Depends(getCurrentUser)],
Expand Down Expand Up @@ -98,6 +108,7 @@ def updateARK(
)


@resolverRouter.delete("/ark:/{NAAN}/{postfix}")
@resolverRouter.delete("/ark:{NAAN}/{postfix}")
def deleteARK(
currentUser: Annotated[UserWriteModel, Depends(getCurrentUser)],
Expand Down
Loading