mirror of
https://github.com/bellingcat/auto-archiver-api.git
synced 2026-06-11 13:08:34 +03:00
Standardize router names (#70)
This commit is contained in:
4
app/shared/constants.py
Normal file
4
app/shared/constants.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# Statuses
|
||||
STATUS_FAILURE = "FAILURE"
|
||||
STATUS_PENDING = "PENDING"
|
||||
STATUS_SUCCESS = "SUCCESS"
|
||||
@@ -15,7 +15,7 @@ def test_submit_manual_archive_not_user_auth(client_with_auth, test_no_auth):
|
||||
|
||||
|
||||
@patch(
|
||||
"app.web.endpoints.interoperability.business_logic",
|
||||
"app.web.routers.interoperability.business_logic",
|
||||
return_value=MagicMock(
|
||||
get_store_archive_until=MagicMock(return_value=datetime)
|
||||
),
|
||||
@@ -103,7 +103,7 @@ def test_submit_manual_archive_invalid_json(client_with_token):
|
||||
|
||||
|
||||
@patch(
|
||||
"app.web.endpoints.interoperability.business_logic.get_store_archive_until",
|
||||
"app.web.routers.interoperability.business_logic.get_store_archive_until",
|
||||
side_effect=AssertionError("AssertionError"),
|
||||
)
|
||||
def test_submit_manual_archive_no_store_until(
|
||||
@@ -4,6 +4,7 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.shared.constants import STATUS_PENDING
|
||||
from app.shared.db import models
|
||||
from app.shared.schemas import TaskResult
|
||||
from app.web.db.user_state import UserState
|
||||
@@ -184,7 +185,7 @@ def test_delete_sheet_endpoint(client_with_auth, db_session):
|
||||
|
||||
|
||||
class TestArchiveUserSheetEndpoint:
|
||||
@patch("app.web.endpoints.sheet.celery", return_value=MagicMock())
|
||||
@patch("app.web.routers.sheet.celery", return_value=MagicMock())
|
||||
def test_normal_flow(self, m_celery, client_with_auth, db_session):
|
||||
db_session.add(
|
||||
models.Sheet(
|
||||
@@ -199,7 +200,7 @@ class TestArchiveUserSheetEndpoint:
|
||||
|
||||
m_signature = MagicMock()
|
||||
m_signature.apply_async.return_value = TaskResult(
|
||||
id="123-taskid", status="PENDING", result=""
|
||||
id="123-taskid", status=STATUS_PENDING, result=""
|
||||
)
|
||||
m_celery.signature.return_value = m_signature
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
from http import HTTPStatus
|
||||
from unittest.mock import patch
|
||||
|
||||
from app.shared.constants import STATUS_FAILURE, STATUS_PENDING, STATUS_SUCCESS
|
||||
|
||||
|
||||
def test_endpoint_task_status_no_auth(client, test_no_auth):
|
||||
test_no_auth(client.get, "/task/test-task-id")
|
||||
|
||||
|
||||
@patch("app.web.endpoints.task.AsyncResult")
|
||||
@patch("app.web.routers.task.AsyncResult")
|
||||
def test_get_status_success(mock_async_result, client_with_auth):
|
||||
mock_async_result.return_value.status = "SUCCESS"
|
||||
mock_async_result.return_value.status = STATUS_SUCCESS
|
||||
mock_async_result.return_value.result = {"data": "some result"}
|
||||
|
||||
response = client_with_auth.get("/task/test-task-id")
|
||||
@@ -16,14 +18,14 @@ def test_get_status_success(mock_async_result, client_with_auth):
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
assert response.json() == {
|
||||
"id": "test-task-id",
|
||||
"status": "SUCCESS",
|
||||
"status": STATUS_SUCCESS,
|
||||
"result": {"data": "some result"},
|
||||
}
|
||||
|
||||
|
||||
@patch("app.web.endpoints.task.AsyncResult")
|
||||
@patch("app.web.routers.task.AsyncResult")
|
||||
def test_get_status_failure(mock_async_result, client_with_auth):
|
||||
mock_async_result.return_value.status = "FAILURE"
|
||||
mock_async_result.return_value.status = STATUS_FAILURE
|
||||
mock_async_result.return_value.result = Exception("Some error")
|
||||
|
||||
response = client_with_auth.get("/task/test-task-id")
|
||||
@@ -31,14 +33,14 @@ def test_get_status_failure(mock_async_result, client_with_auth):
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
assert response.json() == {
|
||||
"id": "test-task-id",
|
||||
"status": "FAILURE",
|
||||
"status": STATUS_FAILURE,
|
||||
"result": {"error": "Some error"},
|
||||
}
|
||||
|
||||
|
||||
@patch("app.web.endpoints.task.AsyncResult")
|
||||
@patch("app.web.routers.task.AsyncResult")
|
||||
def test_get_status_pending(mock_async_result, client_with_auth):
|
||||
mock_async_result.return_value.status = "PENDING"
|
||||
mock_async_result.return_value.status = STATUS_PENDING
|
||||
mock_async_result.return_value.result = None
|
||||
|
||||
response = client_with_auth.get("/task/test-task-id")
|
||||
@@ -46,6 +48,6 @@ def test_get_status_pending(mock_async_result, client_with_auth):
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
assert response.json() == {
|
||||
"id": "test-task-id",
|
||||
"status": "PENDING",
|
||||
"status": STATUS_PENDING,
|
||||
"result": None,
|
||||
}
|
||||
@@ -3,6 +3,7 @@ from http import HTTPStatus
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from app.shared import schemas
|
||||
from app.shared.constants import STATUS_PENDING
|
||||
from app.shared.db import worker_crud
|
||||
from app.shared.schemas import ArchiveCreate, TaskResult
|
||||
from app.web.config import ALLOW_ANY_EMAIL
|
||||
@@ -12,12 +13,12 @@ def test_archive_url_unauthenticated(client, test_no_auth):
|
||||
test_no_auth(client.post, "/url/archive")
|
||||
|
||||
|
||||
@patch("app.web.endpoints.url.UserState")
|
||||
@patch("app.web.endpoints.url.celery", return_value=MagicMock())
|
||||
@patch("app.web.routers.url.UserState")
|
||||
@patch("app.web.routers.url.celery", return_value=MagicMock())
|
||||
def test_archive_url(m_celery, m2, client_with_auth):
|
||||
m_signature = MagicMock()
|
||||
m_signature.apply_async.return_value = TaskResult(
|
||||
id="123-456-789", status="PENDING", result=""
|
||||
id="123-456-789", status=STATUS_PENDING, result=""
|
||||
)
|
||||
m_celery.signature.return_value = m_signature
|
||||
|
||||
@@ -123,7 +124,7 @@ def test_archive_url(m_celery, m2, client_with_auth):
|
||||
assert m_signature.apply_async.call_count == 2
|
||||
|
||||
|
||||
@patch("app.web.endpoints.url.UserState")
|
||||
@patch("app.web.routers.url.UserState")
|
||||
def test_archive_url_quotas(m1, client_with_auth):
|
||||
m_user_state = MagicMock()
|
||||
m1.return_value = m_user_state
|
||||
@@ -152,11 +153,11 @@ def test_archive_url_quotas(m1, client_with_auth):
|
||||
m_user_state.has_quota_max_monthly_mbs.assert_called_once()
|
||||
|
||||
|
||||
@patch("app.web.endpoints.url.celery", return_value=MagicMock())
|
||||
@patch("app.web.routers.url.celery", return_value=MagicMock())
|
||||
def test_archive_url_with_api_token(m_celery, client_with_token):
|
||||
m_signature = MagicMock()
|
||||
m_signature.apply_async.return_value = TaskResult(
|
||||
id="123-456-789", status="PENDING", result=""
|
||||
id="123-456-789", status=STATUS_PENDING, result=""
|
||||
)
|
||||
m_celery.signature.return_value = m_signature
|
||||
response = client_with_token.post(
|
||||
@@ -274,7 +275,7 @@ def test_search_by_url(client_with_auth, client_with_token, db_session):
|
||||
assert len(response.json()) == 10
|
||||
|
||||
|
||||
@patch("app.web.endpoints.url.UserState")
|
||||
@patch("app.web.routers.url.UserState")
|
||||
def test_search_no_read_access(mock_user_state, client_with_auth):
|
||||
mock_user_state.return_value.read = False
|
||||
mock_user_state.return_value.read_public = False
|
||||
@@ -40,7 +40,7 @@ def test_alembic(db_session):
|
||||
|
||||
|
||||
@patch(
|
||||
"app.web.endpoints.url.crud.soft_delete_archive",
|
||||
"app.web.routers.url.crud.soft_delete_archive",
|
||||
side_effect=Exception("mocked error"),
|
||||
)
|
||||
def test_logging_middleware(m1, client_with_auth):
|
||||
|
||||
@@ -9,13 +9,13 @@ from prometheus_fastapi_instrumentator import Instrumentator
|
||||
from app.shared.settings import Settings, get_settings
|
||||
from app.shared.task_messaging import get_celery
|
||||
from app.web.config import API_DESCRIPTION, VERSION
|
||||
from app.web.endpoints.default import default_router
|
||||
from app.web.endpoints.interoperability import interoperability_router
|
||||
from app.web.endpoints.sheet import sheet_router
|
||||
from app.web.endpoints.task import task_router
|
||||
from app.web.endpoints.url import url_router
|
||||
from app.web.events import lifespan
|
||||
from app.web.middleware import logging_middleware
|
||||
from app.web.routers.default import router as default_router
|
||||
from app.web.routers.interoperability import router as interoperability_router
|
||||
from app.web.routers.sheet import router as sheet_router
|
||||
from app.web.routers.task import router as task_router
|
||||
from app.web.routers.url import router as url_router
|
||||
from app.web.security import token_api_key_auth
|
||||
|
||||
|
||||
|
||||
@@ -11,22 +11,22 @@ from app.web.db.user_state import UserState
|
||||
from app.web.security import get_user_state
|
||||
|
||||
|
||||
default_router = APIRouter()
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@default_router.get("/")
|
||||
@router.get("/")
|
||||
async def home() -> JSONResponse:
|
||||
return JSONResponse(
|
||||
{"version": VERSION, "breakingChanges": BREAKING_CHANGES}
|
||||
)
|
||||
|
||||
|
||||
@default_router.get("/health")
|
||||
@router.get("/health")
|
||||
async def health() -> JSONResponse:
|
||||
return JSONResponse({"status": "ok"})
|
||||
|
||||
|
||||
@default_router.get(
|
||||
@router.get(
|
||||
"/user/active", summary="Check if the user is active and can use the tool."
|
||||
)
|
||||
async def active(
|
||||
@@ -35,7 +35,7 @@ async def active(
|
||||
return ActiveUser(active=user.active)
|
||||
|
||||
|
||||
@default_router.get(
|
||||
@router.get(
|
||||
"/user/permissions",
|
||||
summary="Get the user's global 'all' permissions and the permissions for each group they belong to.",
|
||||
)
|
||||
@@ -45,7 +45,7 @@ def get_user_permissions(
|
||||
return user.permissions
|
||||
|
||||
|
||||
@default_router.get(
|
||||
@router.get(
|
||||
"/user/usage",
|
||||
summary="Get the user's monthly URLs/MBs usage along with the total active sheets, breakdown by group.",
|
||||
)
|
||||
@@ -59,6 +59,6 @@ def get_user_usage(
|
||||
return user.usage()
|
||||
|
||||
|
||||
@default_router.get("/favicon.ico", include_in_schema=False)
|
||||
@router.get("/favicon.ico", include_in_schema=False)
|
||||
async def favicon() -> FileResponse:
|
||||
return FileResponse("app/web/static/favicon.ico")
|
||||
@@ -17,13 +17,11 @@ from app.web.security import token_api_key_auth
|
||||
from app.web.utils.misc import get_all_urls
|
||||
|
||||
|
||||
interoperability_router = APIRouter(
|
||||
prefix="/interop", tags=["Interoperability endpoints."]
|
||||
)
|
||||
router = APIRouter(prefix="/interop", tags=["Interoperability endpoints."])
|
||||
|
||||
|
||||
# ----- endpoint to submit data archived elsewhere
|
||||
@interoperability_router.post(
|
||||
@router.post(
|
||||
"/submit-archive",
|
||||
status_code=HTTPStatus.CREATED,
|
||||
summary="Submit a manual archive entry, for data that was archived elsewhere.",
|
||||
@@ -18,14 +18,12 @@ from app.web.db.user_state import UserState
|
||||
from app.web.security import get_user_state
|
||||
|
||||
|
||||
sheet_router = APIRouter(
|
||||
prefix="/sheet", tags=["Google Spreadsheet operations"]
|
||||
)
|
||||
router = APIRouter(prefix="/sheet", tags=["Google Spreadsheet operations"])
|
||||
|
||||
celery = get_celery()
|
||||
|
||||
|
||||
@sheet_router.post(
|
||||
@router.post(
|
||||
"/create",
|
||||
status_code=HTTPStatus.CREATED,
|
||||
summary="Store a new Google Sheet for regular archiving.",
|
||||
@@ -69,7 +67,7 @@ def create_sheet(
|
||||
) from e
|
||||
|
||||
|
||||
@sheet_router.get(
|
||||
@router.get(
|
||||
"/mine",
|
||||
status_code=HTTPStatus.OK,
|
||||
summary="Get the authenticated user's Google Sheets.",
|
||||
@@ -81,7 +79,7 @@ def get_user_sheets(
|
||||
return crud.get_user_sheets(db, user.email)
|
||||
|
||||
|
||||
@sheet_router.delete("/{sheet_id}", summary="Delete a Google Sheet by ID.")
|
||||
@router.delete("/{sheet_id}", summary="Delete a Google Sheet by ID.")
|
||||
def delete_sheet(
|
||||
sheet_id: str,
|
||||
user: UserState = Depends(get_user_state),
|
||||
@@ -92,7 +90,7 @@ def delete_sheet(
|
||||
)
|
||||
|
||||
|
||||
@sheet_router.post(
|
||||
@router.post(
|
||||
"/{sheet_id}/archive",
|
||||
status_code=HTTPStatus.CREATED,
|
||||
summary="Trigger an archiving task for a GSheet you own.",
|
||||
@@ -4,18 +4,19 @@ from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from app.shared import schemas
|
||||
from app.shared.constants import STATUS_FAILURE
|
||||
from app.shared.log import log_error
|
||||
from app.shared.task_messaging import get_celery
|
||||
from app.web.security import get_token_or_user_auth
|
||||
from app.web.utils.misc import custom_jsonable_encoder
|
||||
|
||||
|
||||
task_router = APIRouter(prefix="/task", tags=["Async task operations"])
|
||||
router = APIRouter(prefix="/task", tags=["Async task operations"])
|
||||
|
||||
celery = get_celery()
|
||||
|
||||
|
||||
@task_router.get(
|
||||
@router.get(
|
||||
"/{task_id}",
|
||||
summary="Check the status of an async task by its id, works for URLs and Sheet tasks.",
|
||||
)
|
||||
@@ -24,7 +25,7 @@ def get_status(
|
||||
) -> schemas.TaskResult:
|
||||
task = AsyncResult(task_id, app=celery)
|
||||
try:
|
||||
if task.status == "FAILURE":
|
||||
if task.status == STATUS_FAILURE:
|
||||
# *FAILURE* The task raised an exception, or has exceeded the retry limit.
|
||||
# The :attr:`result` attribute then contains the exception raised by
|
||||
# the task.
|
||||
@@ -43,5 +44,9 @@ def get_status(
|
||||
except Exception as e:
|
||||
log_error(e)
|
||||
return JSONResponse(
|
||||
{"id": task_id, "status": "FAILURE", "result": {"error": str(e)}}
|
||||
{
|
||||
"id": task_id,
|
||||
"status": STATUS_FAILURE,
|
||||
"result": {"error": str(e)},
|
||||
}
|
||||
)
|
||||
@@ -18,12 +18,12 @@ from app.web.security import get_token_or_user_auth, get_user_state
|
||||
from app.web.utils.misc import convert_priority_to_queue_dict
|
||||
|
||||
|
||||
url_router = APIRouter(prefix="/url", tags=["Single URL operations"])
|
||||
router = APIRouter(prefix="/url", tags=["Single URL operations"])
|
||||
|
||||
celery = get_celery()
|
||||
|
||||
|
||||
@url_router.post(
|
||||
@router.post(
|
||||
"/archive",
|
||||
status_code=HTTPStatus.CREATED,
|
||||
summary="Submit a single URL archive request, starts an archiving task.",
|
||||
@@ -77,7 +77,7 @@ def archive_url(
|
||||
)
|
||||
|
||||
|
||||
@url_router.get("/search", summary="Search for archive entries by URL.")
|
||||
@router.get("/search", summary="Search for archive entries by URL.")
|
||||
def search_by_url(
|
||||
url: str,
|
||||
skip: int = 0,
|
||||
@@ -110,9 +110,7 @@ def search_by_url(
|
||||
)
|
||||
|
||||
|
||||
@url_router.delete(
|
||||
"/{archive_id}", summary="Delete a single URL archive by id."
|
||||
)
|
||||
@router.delete("/{archive_id}", summary="Delete a single URL archive by id.")
|
||||
def delete_archive(
|
||||
archive_id: str,
|
||||
user: UserState = Depends(get_user_state),
|
||||
Reference in New Issue
Block a user