Skip to content

Commit

Permalink
check how / if ThreadedMotoServer works out in CI
Browse files Browse the repository at this point in the history
  • Loading branch information
metazool committed Aug 21, 2024
1 parent c4bb81d commit 145e28b
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 42 deletions.
10 changes: 7 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
My Project documentation
Object Store API documentation
========================

Welcome to the docs, write your introduction here and explain why your code is great!
This API is intended to simplify the creation and management of research data held in "object storage" - typically using s3, a de facto standard made popular by Amazon AWS, which the JASMIN data analysis facility makes freely available for research purposes.

.. toctree::
See the `Object Store tutorial <https://github.com/NERC-CEH/object_store_tutorial/>`_ project for guidance and examples of working with the object storage in JASMIN.

This

.. toctree ::
:maxdepth: 2
:caption: Contents:
Expand Down
40 changes: 31 additions & 9 deletions src/os_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@

CONCURRENCY_LIMIT = 200 # Adjust this value based on your server capabilities

session = aioboto3.Session()

# Wrapped in function to use as dependency_override
def boto_session() -> aioboto3.Session:
return aioboto3.Session()


session = boto_session()


@app.get("/", include_in_schema=False)
Expand All @@ -86,14 +92,18 @@ async def create_bucket(bucket_name: str = Query("", description="")) -> JSONRes
content={"message": f"Bucket '{bucket_name}' created successfully"},
)
except s3_client.exceptions.BucketAlreadyExists:
return JSONResponse(status_code=409, content={f"Bucket {bucket_name} already exists."})
return JSONResponse(
status_code=409, content={f"Bucket {bucket_name} already exists."}
)
except s3_client.exceptions.BucketAlreadyOwnedByYou:
return JSONResponse(
status_code=409,
content={f"Bucket {bucket_name} is already owned by you."},
)
except Exception as e:
return JSONResponse(status_code=500, content={f"Error creating bucket: {str(e)}"})
return JSONResponse(
status_code=500, content={f"Error creating bucket: {str(e)}"}
)


@app.post("/generate-presigned-url/", tags=["Data"])
Expand Down Expand Up @@ -121,9 +131,13 @@ async def generate_presigned_url(

return JSONResponse(status_code=200, content=presigned_url)
except NoCredentialsError:
return JSONResponse(status_code=403, content={"error": "No AWS credentials found"})
return JSONResponse(
status_code=403, content={"error": "No AWS credentials found"}
)
except PartialCredentialsError:
return JSONResponse(status_code=403, content={"error": "Incomplete AWS credentials"})
return JSONResponse(
status_code=403, content={"error": "Incomplete AWS credentials"}
)
except Exception as e:
return JSONResponse(status_code=500, content={"error": str(e)})

Expand Down Expand Up @@ -165,10 +179,14 @@ async def upload_file(s3_bucket_name: str, key: str, file: UploadFile) -> None:
) as s3_client:
try:
# Upload updated file to S3
await s3_client.upload_fileobj(file.file, s3_bucket_name, f"{key}/{file.filename}")
await s3_client.upload_fileobj(
file.file, s3_bucket_name, f"{key}/{file.filename}"
)
# print(f"File {key}/{file.filename} uploaded successfully.")
except Exception as e:
logger.error("Error when uploading %s to %s/%s.", file.filename, s3_bucket_name, key)
logger.error(
"Error when uploading %s to %s/%s.", file.filename, s3_bucket_name, key
)
return JSONResponse(
status_code=500,
content={"message": f"Error uploading {key}/{file.filename}: {e}"},
Expand Down Expand Up @@ -198,9 +216,13 @@ async def check_file_exist(filename: str = Form(...)) -> JSONResponse:
return JSONResponse(status_code=200, content=message)
return JSONResponse(status_code=500, content={"message": f"{e}"})
except NoCredentialsError:
return JSONResponse(status_code=403, content={"message": "No AWS credentials found"})
return JSONResponse(
status_code=403, content={"message": "No AWS credentials found"}
)
except PartialCredentialsError:
return JSONResponse(status_code=403, content={"message": "Incomplete AWS credentials"})
return JSONResponse(
status_code=403, content={"message": "Incomplete AWS credentials"}
)
except Exception as e:
return JSONResponse(status_code=500, content={"message": f"{e}"})

Expand Down
14 changes: 0 additions & 14 deletions tests/conftest.py

This file was deleted.

46 changes: 30 additions & 16 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,56 @@
At level of "do endpoints exist, and resolve"
"""

import os
from os_api.api import app
from fastapi import FastAPI
import pytest
from fastapi.testclient import TestClient
from moto.server import ThreadedMotoServer
import logging

logging.basicConfig(level=logging.INFO)


# TODO resolve how moto should work with aioboto3 Session + fastapi dependency overrides
@pytest.fixture(scope="module")
def moto_server():
"""Fixture to run a mocked AWS server for testing."""
# Note: pass `port=0` to get a random free port.
server = ThreadedMotoServer(port=0)
server.start()
host, port = server.get_host_and_port()
os.environ["AWS_URL_ENDPOINT"] = f"http://{host}:{port}"
yield f"http://{host}:{port}"
server.stop()


client = TestClient(app)


def test_read_main():
response = client.get("/")
assert response.status_code == 200


# @pytest.mark.asyncio # TODO see above, mock bucket
def test_create_bucket():
params = {'bucket_name': 'test_bucket'}
response = client.post('create_bucket', params=params)
assert response
@pytest.mark.asyncio
def test_create_bucket(moto_server):
params = {"bucket_name": "test_bucket"}
response = client.post("/create-bucket/", params=params)
assert response.status_code == 200


def test_generate_presigned_url():
def test_generate_presigned_url(moto_server):
params = {"filename": "demo.txt", "file_type": "text/plain"}
response = client.post("/generate-presigned-url/", data=params)
assert response.status_code == 200

response = client.post('/generate-presigned-url/')
assert response

def test_upload():

response = client.post('/upload/')
response = client.post("/upload/")
assert response


def test_check_file_exist():

response = client.post('/check-file-exist/')
response = client.post("/check-file-exist/")
assert response

def test_import_api_module():
import os_api.api

0 comments on commit 145e28b

Please sign in to comment.