Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redoc #46

Merged
merged 3 commits into from
Mar 15, 2022
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The Data Submission Portal is a website that provides recommendations, best prac
make up
```

Access the OpenAPI docs at http://0.0.0.0:5002/docs
Access the OpenAPI docs at http://0.0.0.0:5002/redoc

#### Run the full stack (with dspfront)
A make command exists for building the dspfront image. It temporarily clones the repository with the default branch and builds the image for you.
Expand Down
26 changes: 22 additions & 4 deletions dspback/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import uvicorn as uvicorn
from fastapi import FastAPI, Request, Response
from fastapi.openapi.utils import get_openapi
from fastapi.staticfiles import StaticFiles
from starlette.middleware.sessions import SessionMiddleware

Expand All @@ -12,10 +13,12 @@

app.mount("/api/schema", StaticFiles(directory="dspback/schemas"), name="schemas")

app.include_router(authentication.router, prefix="/api", tags=["api"])
app.include_router(repository_authorization.router, prefix="/api", tags=["api"])
app.include_router(submissions.router, prefix="/api", tags=["api"])
app.include_router(metadata_class.router, prefix="/api", tags=["api"])
app.include_router(authentication.router, prefix="/api", tags=["Authentication"], include_in_schema=False)
app.include_router(
repository_authorization.router, prefix="/api", tags=["Repository Authorization"], include_in_schema=False
)
app.include_router(metadata_class.router, prefix="/api")
app.include_router(submissions.router, prefix="/api", tags=["Submissions"])


@app.middleware("http")
Expand All @@ -29,5 +32,20 @@ async def db_session_middleware(request: Request, call_next):
return response


openapi_schema = get_openapi(
title="Data Submission Portal API",
version="1.0",
description="Standardized interface with validation for managing metadata across repositories",
routes=app.routes,
)
openapi_schema["info"]["contact"] = {
"name": "Learn more about this API",
"url": "https://github.com/cznethub/dspback",
"email": "[email protected]",
}


app.openapi_schema = openapi_schema

if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=5002)
118 changes: 101 additions & 17 deletions dspback/routers/metadata_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,14 @@ class HydroShareMetadataRoutes(MetadataRoutes):
response_model = ResourceMetadata
repository_type = RepositoryType.HYDROSHARE

@router.post('/metadata/hydroshare', response_model_exclude_unset=True, response_model=response_model)
@router.post(
'/metadata/hydroshare',
response_model_exclude_unset=True,
response_model=response_model,
tags=["HydroShare"],
summary="Create a HydroShare resource",
description="Validates the incoming metadata, creates a new HydroShare resource and creates a submission record.",
)
async def create_metadata_repository(self, metadata: request_model):
response = requests.post(
self.create_url,
Expand All @@ -98,7 +105,14 @@ async def create_metadata_repository(self, metadata: request_model):

return JSONResponse(json_metadata, status_code=201)

@router.put('/metadata/hydroshare/{identifier}', response_model_exclude_unset=True, response_model=response_model)
@router.put(
'/metadata/hydroshare/{identifier}',
response_model_exclude_unset=True,
response_model=response_model,
tags=["HydroShare"],
summary="Update a HydroShare resource",
description="Validates the incoming metadata and updates the HydroShare resource associated with the provided identifier.",
)
async def update_metadata(self, metadata: request_model, identifier):
response = requests.put(
self.update_url % identifier,
Expand Down Expand Up @@ -126,14 +140,26 @@ async def _retrieve_metadata_from_repository(self, identifier):
json_metadata["additional_metadata"] = [{"key": key, "value": value} for key, value in as_dict.items()]
return json_metadata

@router.get('/metadata/hydroshare/{identifier}', response_model_exclude_unset=True, response_model=response_model)
@router.get(
'/metadata/hydroshare/{identifier}',
response_model_exclude_unset=True,
response_model=response_model,
tags=["HydroShare"],
summary="Get a HydroShare resource",
description="Retrieves the metadata for the HydroShare resource.",
)
async def get_metadata_repository(self, identifier):
json_metadata = await self._retrieve_metadata_from_repository(identifier)
# workaround for rendering dict with key/value forms
await self.submit(identifier=identifier, json_metadata=json_metadata)
return json_metadata

@router.delete('/metadata/hydroshare/{identifier}')
@router.delete(
'/metadata/hydroshare/{identifier}',
tags=["HydroShare"],
summary="Delete a HydroShare resource",
description="Deletes the HydroShare resource along with the submission record.",
)
async def delete_metadata_repository(self, identifier):
response = requests.delete(self.delete_url % identifier, params={"access_token": self.access_token})

Expand All @@ -147,6 +173,9 @@ async def delete_metadata_repository(self, identifier):
name="submit",
response_model_exclude_unset=True,
response_model=response_model,
tags=["HydroShare"],
summary="Register a HydroShare resource",
description="Creates a submission record of the HydroShare resource.",
)
async def submit_repository_record(self, identifier: str):
json_metadata = await self.submit(identifier)
Expand All @@ -160,7 +189,14 @@ class ZenodoMetadataRoutes(MetadataRoutes):
response_model = ZenodoDatasetsSchemaForCzNetV100
repository_type = RepositoryType.ZENODO

@router.post('/metadata/zenodo', response_model_exclude_unset=True, response_model=response_model)
@router.post(
'/metadata/zenodo',
response_model_exclude_unset=True,
response_model=response_model,
tags=["Zenodo"],
summary="Create a Zenodo resource",
description="Validates the incoming metadata, creates a new Zenodo record and creates a submission record.",
)
async def create_metadata_repository(self, metadata: request_model):
metadata_json = {"metadata": json.loads(metadata.json(exclude_none=True))}
response = requests.post(
Expand All @@ -183,6 +219,9 @@ async def create_metadata_repository(self, metadata: request_model):
'/metadata/zenodo/{identifier}',
response_model_exclude_unset=True,
response_model=response_model,
tags=["Zenodo"],
summary="Update a Zenodo record",
description="Validates the incoming metadata and updates the Zenodo record associated with the provided identifier.",
)
async def update_metadata(self, metadata: request_model, identifier):
existing_metadata = await self.get_metadata_repository(identifier)
Expand Down Expand Up @@ -210,13 +249,25 @@ async def _retrieve_metadata_from_repository(self, identifier):
json_metadata = json.loads(response.text)
return json_metadata

@router.get('/metadata/zenodo/{identifier}', response_model_exclude_unset=True, response_model=response_model)
@router.get(
'/metadata/zenodo/{identifier}',
response_model_exclude_unset=True,
response_model=response_model,
tags=["Zenodo"],
summary="Get a Zenodo record",
description="Retrieves the metadata for the Zenodo record.",
)
async def get_metadata_repository(self, identifier):
json_metadata = await self._retrieve_metadata_from_repository(identifier)
await self.submit(identifier=identifier, json_metadata=json_metadata)
return json_metadata["metadata"]

@router.delete('/metadata/zenodo/{identifier}')
@router.delete(
'/metadata/zenodo/{identifier}',
tags=["Zenodo"],
summary="Delete a Zenodo record",
description="Deletes the Zenodo record along with the submission record.",
)
async def delete_metadata_repository(self, identifier):
response = requests.delete(self.delete_url % identifier, params={"access_token": self.access_token})

Expand All @@ -225,7 +276,14 @@ async def delete_metadata_repository(self, identifier):

delete_submission(self.db, self.repository_type, identifier, self.user)

@router.put('/submit/zenodo/{identifier}', name="submit", response_model=SubmissionBase)
@router.put(
'/submit/zenodo/{identifier}',
name="submit",
response_model=SubmissionBase,
tags=["Zenodo"],
summary="Register a Zenodo record",
description="Creates a submission record of the Zenodo record.",
)
async def submit_repository_record(self, identifier: str):
json_metadata = await self.submit(identifier)
return json_metadata["metadata"]
Expand All @@ -239,26 +297,26 @@ class EarthChemMetadataRoutes(MetadataRoutes):
response_model = Ecl20
repository_type = RepositoryType.EARTHCHEM

@router.post('/metadata/earthchem')
@router.post('/metadata/earthchem', tags=["EarthChem"])
async def create_metadata_repository(self, metadata: request_model) -> response_model:
raise NotImplementedError("EarthChem metadata endpoints are not implemented yet")

@router.put('/metadata/earthchem/{identifier}')
@router.put('/metadata/earthchem/{identifier}', tags=["EarthChem"])
async def update_metadata(self, metadata: request_model_update, identifier) -> response_model:
raise NotImplementedError("EarthChem metadata endpoints are not implemented yet")

async def _retrieve_metadata_from_repository(self, identifier):
raise NotImplementedError("EarthChem metadata endpoints are not implemented yet")

@router.get('/metadata/earthchem/{identifier}')
@router.get('/metadata/earthchem/{identifier}', tags=["EarthChem"])
async def get_metadata_repository(self, identifier) -> response_model:
raise NotImplementedError("EarthChem metadata endpoints are not implemented yet")

@router.delete('/metadata/earthchem/{identifier}')
@router.delete('/metadata/earthchem/{identifier}', tags=["EarthChem"])
async def delete_metadata_repository(self, identifier):
raise NotImplementedError("EarthChem metadata endpoints are not implemented yet")

@router.put('/submit/earthchem/{identifier}', name="submit")
@router.put('/submit/earthchem/{identifier}', name="submit", tags=["EarthChem"])
async def submit_repository_record(self, identifier: str):
raise NotImplementedError("EarthChem metadata endpoints are not implemented yet")

Expand All @@ -270,24 +328,50 @@ class ExternalMetadataRoutes(MetadataRoutes):
response_model = GenericDatasetSchemaForCzNetDataSubmissionPortalV100
repository_type = RepositoryType.EXTERNAL

@router.post('/metadata/external', response_model_exclude_unset=True, response_model=response_model)
@router.post(
'/metadata/external',
response_model_exclude_unset=True,
response_model=response_model,
tags=["External"],
summary="Create an external record",
description="Create an external record along with the submission record.",
)
async def create_metadata_repository(self, metadata: request_model):
metadata.identifier = str(uuid.uuid4())
metadata_json = json.loads(metadata.json())
metadata_json = await self.submit(metadata.identifier, metadata_json)
return JSONResponse(metadata_json, status_code=201)

@router.put('/metadata/external/{identifier}', response_model_exclude_unset=True, response_model=response_model)
@router.put(
'/metadata/external/{identifier}',
response_model_exclude_unset=True,
response_model=response_model,
tags=["External"],
summary="Update an external record",
description="update an external record along with the submission record.",
)
async def update_metadata(self, metadata: request_model, identifier):
return await self.submit(identifier, metadata.dict())

@router.get('/metadata/external/{identifier}', response_model_exclude_unset=True, response_model=response_model)
@router.get(
'/metadata/external/{identifier}',
response_model_exclude_unset=True,
response_model=response_model,
tags=["External"],
summary="Get an external record",
description="Get an external record along with the submission record.",
)
async def get_metadata_repository(self, identifier):
submission = self.user.submission(self.db, identifier)
metadata_json_str = submission.metadata_json
metadata_json = json.loads(metadata_json_str)
return metadata_json

@router.delete('/metadata/external/{identifier}')
@router.delete(
'/metadata/external/{identifier}',
tags=["External"],
summary="Delete an external record",
description="Deletes an external record.",
)
async def delete_metadata_repository(self, identifier):
delete_submission(self.db, self.repository_type, identifier, self.user)
3 changes: 3 additions & 0 deletions nginx/nginx-local.conf
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ http {

location /docs {
proxy_pass http://dspback:5002;
}

location /redoc {
proxy_pass http://dspback:5002;
}

location /sockjs-node {
Expand Down