Skip to content

Commit

Permalink
Post refactor release
Browse files Browse the repository at this point in the history
  • Loading branch information
wshayes committed Dec 21, 2020
1 parent 859ff4a commit e184501
Show file tree
Hide file tree
Showing 53 changed files with 488 additions and 347 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Expand Up @@ -16,7 +16,7 @@
"statusBarItem.hoverBackground": "#5ce637",
"statusBar.foreground": "#15202b"
},
"python.pythonPath": ".venv/bin/python",
"python.pythonPath": ".venv/bin/python3",
"python.analysis.openFilesOnly": false,
"python.analysis.memory.keepLibraryLocalVariables": true,
"python.autoComplete.addBrackets": true,
Expand Down
1 change: 0 additions & 1 deletion api/app/core/middleware.py
Expand Up @@ -3,7 +3,6 @@
import time

# Third Party
# Local Imports
from loguru import logger
from starlette.middleware.base import BaseHTTPMiddleware

Expand Down
21 changes: 18 additions & 3 deletions api/app/main.py
Expand Up @@ -7,9 +7,9 @@
import time

# Third Party
# Third Party Imports
from fastapi import FastAPI
from fastapi import __version__ as fastapi_version
from fastapi.responses import JSONResponse
from loguru import logger
from starlette.middleware.cors import CORSMiddleware
from starlette.middleware.gzip import GZipMiddleware
Expand All @@ -19,17 +19,16 @@
from starlette_prometheus import PrometheusMiddleware, metrics

# Local
# Local Imports
import bel.core.settings as settings
from bel.__version__ import __version__ as version
from bel.api.core.middleware import StatsMiddleware
from bel.api.endpoints.bel import router as bel_router
from bel.api.endpoints.belspec import router as belspec_router
from bel.api.endpoints.info import router as info_router
from bel.api.endpoints.nanopubs import router as nanopubs_router
from bel.api.endpoints.orthology import router as orthology_router
from bel.api.endpoints.pubmed import router as pubmed_router
from bel.api.endpoints.terms import router as terms_router
from core.middleware import StatsMiddleware

logger.remove()

Expand Down Expand Up @@ -58,6 +57,22 @@
version=version,
)

# Added user_flag to HTTPException
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
headers = getattr(exc, "headers", None)
if headers:
return JSONResponse(
{"detail": exc.detail, "user_flag": exc.user_flag},
status_code=exc.status_code,
headers=headers,
)
else:
return JSONResponse(
{"detail": exc.detail, "user_flag": exc.user_flag}, status_code=exc.status_code
)


logger.info("Starting BEL API")
logger.info(f"Fast API Version: {fastapi_version}")

Expand Down
17 changes: 17 additions & 0 deletions bel/api/core/exceptions.py
@@ -0,0 +1,17 @@
# Standard Library
from typing import Any, Dict, Optional

# Third Party
from fastapi.exceptions import HTTPException as FastAPIHTTPException


class HTTPException(FastAPIHTTPException):
def __init__(
self,
status_code: int,
detail: Any = None,
user_flag: bool = False,
headers: Optional[Dict[str, Any]] = None,
) -> None:
super().__init__(status_code=status_code, detail=detail, headers=headers)
self.user_flag = user_flag
69 changes: 69 additions & 0 deletions bel/api/core/jwt.py
@@ -0,0 +1,69 @@
# Standard Library
from datetime import datetime, timedelta

# Third Party
import jwt
from loguru import logger

# Local
from bel.Config import config

jwt_algorithm = "HS256"


def jwt_create(userid, payload, expiration=None):
"""Create a JSON Web Token
payload: dictionary to be added to JWT
expiration: number of seconds from now to expire token -- defaults to 3600 seconds
"""

if expiration:
exp = datetime.utcnow() + timedelta(seconds=expiration)
else:
exp = datetime.utcnow() + timedelta(seconds=3600)

additional_payload = {
"sub": userid,
"exp": exp,
"iat": datetime.utcnow(),
}

logger.debug("UserId: ", userid, " Payload: ", payload)

payload.update(additional_payload)
token = jwt.encode(
payload, config["secrets"]["bel_api"]["shared_secret"], algorithm=jwt_algorithm
)

return token.decode("utf-8")


def jwt_validate(token):
"""Validates JSON Web Token
Returns:
valid: boolean - true if valid token
token_payload: dict of token payload
"""
try:
jwt.decode(token, config["secrets"]["bel_api"]["shared_secret"], algorithm=jwt_algorithm)
return True
except Exception as e:
return False


def jwt_extract(token):
logger.debug("In JWT Extract")
try:
return (
jwt.decode(
token, config["secrets"]["bel_api"]["shared_secret"], algorithm=jwt_algorithm
),
"",
)
except jwt.ExpiredSignatureError:
logger.debug("JWT expired")
return None, "JWT expired"
except Exception as e:
logger.debug("JWT extraction error ", e)
return None, e
43 changes: 43 additions & 0 deletions bel/api/core/middleware.py
@@ -0,0 +1,43 @@
# Standard Library
import re
import time

# Third Party
from loguru import logger
from starlette.middleware.base import BaseHTTPMiddleware


@logger.catch
class StatsMiddleware(BaseHTTPMiddleware):
"""Get duration of request"""

async def dispatch(self, request, call_next):

url = str(request.url)
method = str(request.method)
t0 = time.time()

response = await call_next(request)

url = url.rstrip("/")
route_name = url.split("/")[-1]

# logger.info("Skipping status/metrics", url=url, route_name=route_name)

if method == "OPTIONS" or route_name in ["status", "metrics", "ping"]:
return response
else:
t1 = time.time()

duration = f"{(t1 - t0) * 1000:.0f}"

logger.opt(exception=True).info(
"Request Metrics {duration_ms} ms, status_code: {status_code} {method} {url}",
# "Request Metrics {duration_ms} ms, status_code: {status_code}",
duration_ms=duration,
status_code=response.status_code,
method=method,
url=url,
)

return response
3 changes: 0 additions & 3 deletions bel/api/endpoints/bel.py
Expand Up @@ -4,11 +4,8 @@
from typing import List, Optional

# Third Party
# Third Party Imports
import fastapi
from fastapi import APIRouter, Depends, Query

# Local Imports
from loguru import logger

# Local
Expand Down
14 changes: 9 additions & 5 deletions bel/api/endpoints/belspec.py
@@ -1,15 +1,15 @@
"""belspec endpoints"""

# Third Party Imports

# Third Party
import fastapi
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi import APIRouter, Depends, Query
from fastapi.responses import PlainTextResponse
from loguru import logger

# Local
# Local Imports
import bel.belspec.crud
from bel.api.core.exceptions import HTTPException
from bel.schemas.belspec import BelSpec, BelSpecVersions, EnhancedBelSpec

router = APIRouter()
Expand Down Expand Up @@ -40,7 +40,9 @@ def get_belspec_version(

belspec = bel.belspec.crud.get_enhanced_belspec(version)
if not belspec:
raise HTTPException(status_code=404, detail=f"No BEL specification for version {version}")
raise HTTPException(
status_code=404, detail=f"No BEL specification for version {version}", user_flag=True
)

return belspec

Expand All @@ -61,7 +63,9 @@ def get_enhanced_belspec(

if not enhanced_belspec:
raise HTTPException(
status_code=404, detail=f"No enhanced BEL specification for version {version}"
status_code=404,
detail=f"No enhanced BEL specification for version {version}",
user_flag=True,
)

return enhanced_belspec
Expand Down
2 changes: 0 additions & 2 deletions bel/api/endpoints/info.py
Expand Up @@ -6,13 +6,11 @@
import re

# Third Party
# Third Party Imports
import fastapi
from fastapi import APIRouter, Depends
from loguru import logger

# Local
# Local Imports
import bel.core.settings as settings
import bel.terms.terms
from bel.__version__ import __version__ as bel_lib_version
Expand Down
7 changes: 3 additions & 4 deletions bel/api/endpoints/nanopubs.py
Expand Up @@ -3,14 +3,13 @@
from typing import List

# Third Party
# Third Party Imports
import fastapi
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends
from loguru import logger

# Local
# Local Imports
import bel.nanopub.validate
from bel.api.core.exceptions import HTTPException
from bel.schemas.nanopubs import NanopubR

router = APIRouter()
Expand All @@ -33,7 +32,7 @@ def nanopub_validation(nanopub: NanopubR, validation_level: str = "complete"):
# data = json.loads(data)

if not nanopub:
raise HTTPException(400, detail=f"No nanopub provided")
raise HTTPException(400, detail=f"No nanopub provided", user_flag=True)

nanopub = bel.nanopub.validate.validate(nanopub, validation_level=validation_level)

Expand Down
2 changes: 0 additions & 2 deletions bel/api/endpoints/orthology.py
Expand Up @@ -4,13 +4,11 @@
from typing import List

# Third Party
# Third Party Imports
import fastapi
from fastapi import APIRouter, Depends, File, Query, UploadFile
from loguru import logger

# Local
# Local Imports
import bel.terms.orthologs

router = APIRouter()
Expand Down
10 changes: 6 additions & 4 deletions bel/api/endpoints/pubmed.py
@@ -1,14 +1,14 @@
"""pubmed endpoints"""

# Third Party Imports

# Third Party
import fastapi
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi import APIRouter, Depends, Query
from loguru import logger

# Local
# Local Imports
import bel.nanopub.pubmed
from bel.api.core.exceptions import HTTPException

router = APIRouter()

Expand All @@ -25,6 +25,8 @@ def get_pubmed_info(
pubmed = bel.nanopub.pubmed.get_pubmed_for_beleditor(pmid, pubmed_only=pubmed_only)

if pubmed is None:
raise HTTPException(status_code=404, detail=f"No Pubmed response for {pmid}")
raise HTTPException(
status_code=404, detail=f"No Pubmed response for {pmid}", user_flag=True
)

return pubmed
6 changes: 2 additions & 4 deletions bel/api/endpoints/resources.py
Expand Up @@ -4,16 +4,14 @@
from typing import List, Optional

# Third Party
# Third Party Imports
import fastapi
from fastapi import APIRouter, Body, Depends, File, HTTPException, Query, UploadFile
from fastapi import APIRouter, Body, Depends, File, Query, UploadFile
from loguru import logger

# Local
import bel.resources.manage

# Local Imports
import bel.terms.terms
from bel.api.core.exceptions import HTTPException
from bel.schemas.terms import Term, TermCompletionResponse

router = APIRouter()
Expand Down
9 changes: 4 additions & 5 deletions bel/api/endpoints/terms.py
Expand Up @@ -4,14 +4,13 @@
from typing import List

# Third Party
# Third Party Imports
import fastapi
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile
from fastapi import APIRouter, Depends, File, Query, UploadFile
from loguru import logger

# Local
# Local Imports
import bel.terms.terms
from bel.api.core.exceptions import HTTPException
from bel.schemas.terms import Term, TermCompletionResponse

router = APIRouter()
Expand Down Expand Up @@ -67,7 +66,7 @@ def get_terms(term_id: str):
terms = bel.terms.terms.get_terms(term_id)

if not terms:
raise HTTPException(status_code=404, detail=f"Term {term_id} not found")
raise HTTPException(status_code=404, detail=f"Term {term_id} not found", user_flag=True)

return terms

Expand All @@ -79,7 +78,7 @@ def get_term_equivalents(term_id: str):
equivalents = bel.terms.terms.get_equivalents(term_id)

if not equivalents:
raise HTTPException(status_code=404, detail=f"Term {term_id} not found")
raise HTTPException(status_code=404, detail=f"Term {term_id} not found", user_flag=True)

return equivalents

Expand Down

0 comments on commit e184501

Please sign in to comment.