diff --git a/src/mavedb/routers/experiments.py b/src/mavedb/routers/experiments.py index ec0d65e0..97f236b2 100644 --- a/src/mavedb/routers/experiments.py +++ b/src/mavedb/routers/experiments.py @@ -2,7 +2,6 @@ from operator import attrgetter from typing import Any, Optional -import pydantic import requests from fastapi import APIRouter, Depends, HTTPException from fastapi.encoders import jsonable_encoder @@ -71,7 +70,7 @@ def list_experiments( query = query.filter( or_( Experiment.created_by_id == user_data.user.id, - Experiment.contributors.any(Contributor.orcid_id == user_data.user.username) + Experiment.contributors.any(Contributor.orcid_id == user_data.user.username), ) ) @@ -225,10 +224,7 @@ async def create_experiment( ] except NonexistentOrcidUserError as e: logger.error(msg="Could not find ORCID user with the provided user ID.", extra=logging_context()) - raise pydantic.ValidationError( - [pydantic.error_wrappers.ErrorWrapper(ValidationError(str(e)), loc="contributors")], - model=experiment.ExperimentCreate, - ) + raise HTTPException(status_code=422, detail=str(e)) try: doi_identifiers = [ @@ -369,10 +365,7 @@ async def update_experiment( ] except NonexistentOrcidUserError as e: logger.error(msg="Could not find ORCID user with the provided user ID.", extra=logging_context()) - raise pydantic.ValidationError( - [pydantic.error_wrappers.ErrorWrapper(ValidationError(str(e)), loc="contributors")], - model=experiment.ExperimentUpdate, - ) + raise HTTPException(status_code=422, detail=str(e)) doi_identifiers = [ await find_or_create_doi_identifier(db, identifier.identifier) diff --git a/src/mavedb/routers/score_sets.py b/src/mavedb/routers/score_sets.py index 9507d3c9..d0b1228f 100644 --- a/src/mavedb/routers/score_sets.py +++ b/src/mavedb/routers/score_sets.py @@ -3,7 +3,6 @@ from typing import Any, List, Optional import pandas as pd -import pydantic from arq import ArqRedis from fastapi import APIRouter, Depends, File, Query, UploadFile, status from fastapi.encoders import jsonable_encoder @@ -23,7 +22,7 @@ RoleRequirer, ) from mavedb.lib.contributors import find_or_create_contributor -from mavedb.lib.exceptions import MixedTargetError, NonexistentOrcidUserError, ValidationError +from mavedb.lib.exceptions import MixedTargetError, NonexistentOrcidUserError from mavedb.lib.identifiers import ( create_external_gene_identifier_offset, find_or_create_doi_identifier, @@ -502,10 +501,7 @@ async def create_score_set( ] except NonexistentOrcidUserError as e: logger.error(msg="Could not find ORCID user with the provided user ID.", extra=logging_context()) - raise pydantic.ValidationError( - [pydantic.error_wrappers.ErrorWrapper(ValidationError(str(e)), loc="contributors")], - model=score_set.ScoreSetCreate, - ) + raise HTTPException(status_code=422, detail=str(e)) doi_identifiers = [ await find_or_create_doi_identifier(db, identifier.identifier) @@ -739,7 +735,7 @@ async def update_score_set_calibration_data( assert_permission(user_data, item, Action.UPDATE) - item.score_calibrations = {k: v.dict() for k, v in calibration_update.items()} + item.score_calibrations = calibration_update db.add(item) db.commit() db.refresh(item) @@ -832,10 +828,7 @@ async def update_score_set( ] except NonexistentOrcidUserError as e: logger.error(msg="Could not find ORCID user with the provided user ID.", extra=logging_context()) - raise pydantic.ValidationError( - [pydantic.error_wrappers.ErrorWrapper(ValidationError(str(e)), loc="contributors")], - model=score_set.ScoreSetUpdate, - ) + raise HTTPException(status_code=422, detail=str(e)) # Score set has not been published and attributes affecting scores may still be edited. if item.private: diff --git a/tests/helpers/util.py b/tests/helpers/util.py index b37c0c4a..27300d23 100644 --- a/tests/helpers/util.py +++ b/tests/helpers/util.py @@ -11,6 +11,7 @@ from mavedb.lib.score_sets import columns_for_dataset, create_variants, create_variants_data, csv_data_to_df from mavedb.lib.validation.dataframe import validate_and_standardize_dataframe_pair +from mavedb.lib.exceptions import NonexistentOrcidUserError from mavedb.models.contributor import Contributor from mavedb.models.enums.processing_state import ProcessingState from mavedb.models.score_set import ScoreSet as ScoreSetDbModel @@ -297,6 +298,10 @@ async def awaitable_exception(): return Exception() +def callable_nonexistent_orcid_user_exception(): + raise NonexistentOrcidUserError() + + def update_expected_response_for_created_resources(expected_response, created_experiment, created_score_set): expected_response.update({"urn": created_score_set["urn"]}) expected_response["experiment"].update( diff --git a/tests/routers/test_experiments.py b/tests/routers/test_experiments.py index 3c83c4b8..c2bdeafa 100644 --- a/tests/routers/test_experiments.py +++ b/tests/routers/test_experiments.py @@ -8,6 +8,7 @@ import requests import requests_mock +from mavedb.lib.exceptions import NonexistentOrcidUserError from mavedb.lib.validation.urn_re import MAVEDB_TMP_URN_RE from mavedb.models.experiment import Experiment as ExperimentDbModel from mavedb.models.experiment_set import ExperimentSet as ExperimentSetDbModel @@ -85,6 +86,22 @@ def test_create_experiment_with_contributor(client, setup_router_db): assert (key, expected_response[key]) == (key, response_data[key]) +def test_cannot_create_experiment_with_nonexistent_contributor(client, setup_router_db): + experiment = deepcopy(TEST_MINIMAL_EXPERIMENT) + experiment.update({"contributors": [{"orcid_id": TEST_ORCID_ID}]}) + + with patch( + "mavedb.lib.orcid.fetch_orcid_user", + side_effect=NonexistentOrcidUserError(f"No ORCID user was found for ORCID ID {TEST_ORCID_ID}."), + ): + response = client.post("/api/v1/experiments/", json=experiment) + + assert response.status_code == 422 + response_data = response.json() + + assert "No ORCID user was found for ORCID ID 1111-1111-1111-1111." in response_data["detail"] + + def test_create_experiment_with_keywords(session, client, setup_router_db): response = client.post("/api/v1/experiments/", json=TEST_EXPERIMENT_WITH_KEYWORD) assert response.status_code == 200 @@ -586,6 +603,50 @@ def test_can_edit_private_experiment(client, setup_router_db, test_field, test_v assert (test_field, response_data[test_field]) == (test_field, test_value) +def test_can_add_contributor_to_own_private_experiment(client, setup_router_db): + experiment = create_experiment(client) + experiment_post_payload = experiment.copy() + experiment_post_payload.update({"contributors": [{"orcid_id": TEST_ORCID_ID}]}) + + with patch( + "mavedb.lib.orcid.fetch_orcid_user", + lambda orcid_id: OrcidUser(orcid_id=orcid_id, given_name="ORCID", family_name="User"), + ): + response = client.put(f"/api/v1/experiments/{experiment['urn']}", json=experiment_post_payload) + + assert response.status_code == 200 + response_data = response.json() + jsonschema.validate(instance=response_data, schema=Experiment.model_json_schema()) + assert ("contributors", response_data["contributors"]) == ( + "contributors", + [ + { + "recordType": "Contributor", + "orcidId": TEST_ORCID_ID, + "givenName": "ORCID", + "familyName": "User", + } + ], + ) + + +def test_cannot_add_nonexistent_contributor_to_experiment(client, setup_router_db): + experiment = create_experiment(client) + experiment_post_payload = experiment.copy() + experiment_post_payload.update({"contributors": [{"orcid_id": TEST_ORCID_ID}]}) + + with patch( + "mavedb.lib.orcid.fetch_orcid_user", + side_effect=NonexistentOrcidUserError(f"No ORCID user was found for ORCID ID {TEST_ORCID_ID}."), + ): + response = client.put(f"/api/v1/experiments/{experiment['urn']}", json=experiment_post_payload) + + assert response.status_code == 422 + response_data = response.json() + + assert "No ORCID user was found for ORCID ID 1111-1111-1111-1111." in response_data["detail"] + + @pytest.mark.parametrize( "test_field,test_value", [ diff --git a/tests/routers/test_score_set.py b/tests/routers/test_score_set.py index 2360cd65..32f38e6e 100644 --- a/tests/routers/test_score_set.py +++ b/tests/routers/test_score_set.py @@ -10,6 +10,7 @@ from sqlalchemy import select from mavedb.lib.validation.urn_re import MAVEDB_TMP_URN_RE, MAVEDB_SCORE_SET_URN_RE, MAVEDB_EXPERIMENT_URN_RE +from mavedb.lib.exceptions import NonexistentOrcidUserError from mavedb.models.enums.processing_state import ProcessingState from mavedb.models.enums.target_category import TargetCategory from mavedb.models.experiment import Experiment as ExperimentDbModel @@ -132,6 +133,23 @@ def test_create_score_set_with_contributor(client, setup_router_db): assert response.status_code == 200 +def test_cannot_create_score_set_with_nonexistent_contributor(client, setup_router_db): + experiment = create_experiment(client) + score_set = deepcopy(TEST_MINIMAL_SEQ_SCORESET) + score_set["experimentUrn"] = experiment["urn"] + score_set.update({"contributors": [{"orcid_id": TEST_ORCID_ID}]}) + + with patch( + "mavedb.lib.orcid.fetch_orcid_user", + side_effect=NonexistentOrcidUserError(f"No ORCID user was found for ORCID ID {TEST_ORCID_ID}."), + ): + response = client.post("/api/v1/score-sets/", json=score_set) + + assert response.status_code == 422 + response_data = response.json() + assert "No ORCID user was found for ORCID ID 1111-1111-1111-1111." in response_data["detail"] + + def test_create_score_set_with_score_range(client, setup_router_db): experiment = create_experiment(client) score_set = deepcopy(TEST_MINIMAL_SEQ_SCORESET) @@ -381,6 +399,27 @@ def test_cannot_update_score_set_target_data_after_publication( assert camelize(attribute) not in response_data.keys() +def test_cannot_update_score_set_with_nonexistent_contributor( + client, + setup_router_db, +): + experiment = create_experiment(client) + score_set = create_seq_score_set(client, experiment["urn"]) + + score_set_update_payload = deepcopy(TEST_MINIMAL_SEQ_SCORESET) + score_set_update_payload.update({"contributors": [{"orcid_id": TEST_ORCID_ID}]}) + + with patch( + "mavedb.lib.orcid.fetch_orcid_user", + side_effect=NonexistentOrcidUserError(f"No ORCID user was found for ORCID ID {TEST_ORCID_ID}."), + ): + response = client.put(f"/api/v1/score-sets/{score_set['urn']}", json=score_set_update_payload) + + assert response.status_code == 422 + response_data = response.json() + assert "No ORCID user was found for ORCID ID 1111-1111-1111-1111." in response_data["detail"] + + ######################################################################################################################## # Score set fetching ########################################################################################################################