Skip to content

Commit

Permalink
G3-9 Initial
Browse files Browse the repository at this point in the history
  • Loading branch information
francastell committed Dec 19, 2023
1 parent 90a56bd commit d395868
Show file tree
Hide file tree
Showing 14 changed files with 1,120 additions and 750 deletions.
1,572 changes: 835 additions & 737 deletions poetry.lock

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ packages = [
]

[tool.poetry.dependencies]
python = "^3.9"
#python = "^3.9"
python = "^3.10.9"

geneweaver-core = "^0.8.0a0"
fastapi = {extras = ["all"], version = "^0.99.1"}
uvicorn = {extras = ["standard"], version = "^0.24.0"}
uvicorn = {extras = ["standard"], version = "^0.23.2"}

#fastapi = "^0.79.0"
#fastapi = {version = "^0.79.0"}
#uvicorn = {extras = ["standard"], version = "^0.18.2"}

geneweaver-db = "^0.2.0a0"
psycopg-pool = "^3.1.7"
requests = "^2.31.0"
Expand All @@ -20,6 +27,7 @@ python-jose = {extras = ["cryptography"], version = "^3.3.0"}
[tool.poetry.group.dev.dependencies]
geneweaver-testing = "^0.0.3"
pytest-asyncio = "^0.21.0"
magicmock = "^0.03"

[tool.ruff]
select = ['F', 'E', 'W', 'A', 'C90', 'N', 'B', 'ANN', 'D', 'I', 'ERA', 'PD', 'NPY', 'PT']
Expand Down
29 changes: 19 additions & 10 deletions src/geneweaver/api/controller/genesets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Security
from geneweaver.api import dependencies as deps
from geneweaver.api.schemas.auth import UserInternal
from fastapi.logger import logger
from geneweaver.core.schema.geneset import GenesetUpload
from geneweaver.db import geneset as db_geneset
from geneweaver.db import geneset_value as db_geneset_value
from geneweaver.db.geneset import is_readable

router = APIRouter(prefix="/genesets")
from geneweaver.api import dependencies as deps
from geneweaver.api.schemas.auth import UserInternal
from geneweaver.api.services import geneset as genset_service
from . import message as api_message

router = APIRouter(prefix="/genesets")

@router.get("")
def get_visible_genesets(
Expand All @@ -26,16 +28,23 @@ def get_visible_genesets(
def get_geneset(
geneset_id: int,
user: UserInternal = Security(deps.full_user),
cursor: Optional[deps.Cursor] = Depends(deps.cursor),
cursor: Optional[deps.Cursor] = Depends(deps.cursor)
) -> dict:
"""Get a geneset by ID."""
if not is_readable(cursor, user.id, geneset_id):
raise HTTPException(status_code=403, detail="Forbidden")
try:
response = genset_service.get_geneset(geneset_id, user, cursor)

except Exception as err:
logger.error(err)
raise HTTPException(status_code=500, detail=api_message.UNEXPECTED_ERROR)

geneset = db_geneset.by_id(cursor, geneset_id)
geneset_values = db_geneset_value.by_geneset_id(cursor, geneset_id)
return {"geneset": geneset, "geneset_values": geneset_values}
if "error" in response:
if response.get("message") == api_message.ACCESS_FORBIDEN:
raise HTTPException(status_code=403, detail=api_message.ACCESS_FORBIDEN)
else:
raise HTTPException(status_code=500, detail=api_message.ACCESS_FORBIDEN)

return response

@router.post("")
def upload_geneset(
Expand Down
6 changes: 6 additions & 0 deletions src/geneweaver/api/controller/message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Costant key/value messages"""


##Errors
ACCESS_FORBIDEN="Forbidden"
UNEXPECTED_ERROR="Unexpected Error"
2 changes: 1 addition & 1 deletion src/geneweaver/api/core/config_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def assemble_db_connection(
"openid profile email": "read",
}
JWT_PERMISSION_PREFIX: str = "approle"
AUTH_CLIENT_ID: str = "oVm9omUtLBpVyL7YfJA8gp3hHaHwyVt8"
AUTH_CLIENT_ID: str = "T7bj6wlmtVcAN2O6kzDRwPVFyIj4UQNs"

class Config:
"""Configuration for the BaseSettings class."""
Expand Down
45 changes: 45 additions & 0 deletions src/geneweaver/api/services/geneset.py
Original file line number Diff line number Diff line change
@@ -1 +1,46 @@
"""Service functions for dealing with genesets."""

from fastapi.logger import logger
from geneweaver.db import geneset as db_geneset
from geneweaver.db import geneset_value as db_geneset_value
from geneweaver.db.geneset import is_readable as db_is_readable

from geneweaver.api import dependencies as deps
from geneweaver.api.controller import message
from geneweaver.api.schemas.auth import User


def get_geneset(
geneset_id: int,
user: User,
cursor: deps.Cursor
) -> dict:

"""Get a geneset by ID."""
try:
if not is_geneset_readable_by_user(geneset_id, user, cursor):
return {"error": True, "message": message.ACCESS_FORBIDEN}

geneset = db_geneset.by_id(cursor, geneset_id)
geneset_values = db_geneset_value.by_geneset_id(cursor, geneset_id)
return {"geneset": geneset, "geneset_values": geneset_values}

except Exception as err:
logger.error(err)
raise err

def is_geneset_readable_by_user(
geneset_id: int,
user: User,
cursor: deps.Cursor
) -> bool:

""" check if the user can read the geneset from DB"""
readable: bool = False
try:
readable = db_is_readable(cursor, user.id, geneset_id)
except Exception as err:
logger.error(err)
raise err

return readable
2 changes: 2 additions & 0 deletions tests/controllers/.pytest_cache/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Created by pytest automatically.
v
4 changes: 4 additions & 0 deletions tests/controllers/.pytest_cache/CACHEDIR.TAG
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by pytest.
# For information about cache directory tags, see:
# https://bford.info/cachedir/spec.html
8 changes: 8 additions & 0 deletions tests/controllers/.pytest_cache/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# pytest cache directory #

This directory contains data from the pytest's cache plugin,
which provides the `--lf` and `--ff` options, as well as the `cache` fixture.

**Do not** commit this to version control.

See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information.
Empty file added tests/controllers/__init__.py
Empty file.
72 changes: 72 additions & 0 deletions tests/controllers/test_genesets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import importlib.resources
import json
from unittest.mock import Mock
from unittest.mock import patch

import psycopg
import pytest
from fastapi.testclient import TestClient

from geneweaver.api.dependencies import full_user, cursor
from geneweaver.api.main import app

client = TestClient(app)

### Load test data
# Opening JSON file
str_json = importlib.resources.read_text("tests.data", "response_geneset_1234.json")
# returns JSON string as a dictionary
test_data = json.loads(str_json)

response_mock = Mock()
response_mock.status_code = 200
response_mock.json.return_value = test_data

### Mock dependencies
m1 = Mock()
def mock_full_user() -> m1.AsyncMock:
return m1
m2 = Mock()
def mock_cursor() -> psycopg.Cursor:
return m2.AsyncMock()
m3 = Mock()
def mock_cursor_obj() -> psycopg.Cursor:
return m3.AsyncMock()

app.dependency_overrides.update(
{full_user: mock_full_user, cursor: mock_cursor}
)

@patch.object(client, 'get', return_value=response_mock)
def test_get_geneset(mock_client):
response = client.get("/genesets/1234")
assert response.status_code == 200

@patch('geneweaver.api.services.geneset.get_geneset')
@patch('geneweaver.api.services.geneset.is_geneset_readable_by_user')
def test_get_geneset_response(mock_genset_is_readable, mock_get_genenset):
mock_genset_is_readable.return_value = True
mock_get_genenset.return_value = test_data

response = client.get("/genesets/1234")
assert response.status_code == 200
assert response.json() == test_data

@patch('geneweaver.api.services.geneset.db_is_readable')
def test_get_geneset_forbidden(mock_genset_is_readable):

mock_genset_is_readable.return_value = False
response = client.get("/genesets/1234")

assert response.json() == {'detail': 'Forbidden'}
assert response.status_code == 403

@patch('geneweaver.api.services.geneset.db_is_readable')
def test_get_geneset_unexpected_error(mock_genset_is_readable):

mock_genset_is_readable.side_effect = Exception

with pytest.raises(Exception):
response = client.get("/genesets/1234")
assert response.json() == {'detail': 'Unexpected error'}
assert response.status_code == 500
Empty file added tests/data/__init__.py
Empty file.
50 changes: 50 additions & 0 deletions tests/data/response_geneset_1234.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"geneset": null,
"geneset_values": [
{
"gs_id": 1234,
"ode_gene_id": 70495,
"gsv_value": 1,
"gsv_hits": 1,
"gsv_source_list": [
"PDE4DIP"
],
"gsv_value_list": [
1
],
"gsv_in_threshold": true,
"gsv_date": "2020-05-05",
"ode_ref_id": "PDE4DIP"
},
{
"gs_id": 1234,
"ode_gene_id": 83819,
"gsv_value": 1,
"gsv_hits": 1,
"gsv_source_list": [
"TMEPAI"
],
"gsv_value_list": [
1
],
"gsv_in_threshold": true,
"gsv_date": "2020-05-05",
"ode_ref_id": "PMEPA1"
},
{
"gs_id": 1234,
"ode_gene_id": 90284,
"gsv_value": 1,
"gsv_hits": 1,
"gsv_source_list": [
"PREPL"
],
"gsv_value_list": [
1
],
"gsv_in_threshold": true,
"gsv_date": "2020-05-05",
"ode_ref_id": "PREPL"
}
]
}
68 changes: 68 additions & 0 deletions tests/services/test_genset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import importlib.resources
import json
from unittest.mock import patch

import pytest

import geneweaver.api.controller.message as message
import geneweaver.api.services.geneset as geneset

## Load test data
# Opening JSON file
str_json = importlib.resources.read_text("tests.data", "response_geneset_1234.json")
# returns JSON string as a dictionary
test_data = json.loads(str_json)

@patch('geneweaver.api.services.geneset.db_geneset')
@patch('geneweaver.api.services.geneset.db_geneset_value')
@patch('geneweaver.api.services.geneset.is_geneset_readable_by_user')
def test_get_geneset_no_errors(mock_genset_readable_func, mock_db_geneset, mock_db_genset_value):

mock_genset_readable_func.return_value = True
mock_db_geneset.by_id.return_value = {}
mock_db_genset_value.by_id.return_value = {}
response = geneset.get_geneset(1234, None, None)
assert response.get('error') == None


@patch('geneweaver.api.services.geneset.is_geneset_readable_by_user')
def test_get_geneset_no_user_access(mock_genset_readable_func):

mock_genset_readable_func.return_value = False
response = geneset.get_geneset(1234, None, None)
assert response.get('error') == True
assert response.get('message') == message.ACCESS_FORBIDEN


@patch('geneweaver.api.services.geneset.db_geneset')
@patch('geneweaver.api.services.geneset.db_geneset_value')
@patch('geneweaver.api.services.geneset.is_geneset_readable_by_user')
def test_get_geneset_returned_values(mock_genset_readable_func, mock_db_genset_value, mock_db_geneset):

mock_genset_readable_func.return_value = True
mock_db_geneset.by_id.return_value = test_data.get('geneset')
mock_db_genset_value.by_geneset_id.return_value = test_data.get('geneset_values')
response = geneset.get_geneset(1234, None, None)

assert response.get('genset') == test_data['geneset']
assert response.get('geneset_values') == test_data['geneset_values']


@patch('geneweaver.api.services.geneset.db_is_readable')
@patch('geneweaver.api.services.geneset.User')
def test_is_redable_by_user(mock_user, mock_genset_is_readable):

mock_genset_is_readable.return_value = True
response = geneset.is_geneset_readable_by_user(1234, mock_user, None)
assert response == True


@patch('geneweaver.db.geneset.is_readable')
@patch('geneweaver.api.services.geneset.User')
def test_is_redable_by_user_error(mock_user, mock_genset_is_readable):
mock_genset_is_readable.sideEffect = Exception

with pytest.raises(Exception):
geneset.is_geneset_readable_by_user(1234, mock_user, None)


0 comments on commit d395868

Please sign in to comment.