Skip to content
This repository has been archived by the owner on May 1, 2023. It is now read-only.

chore: refactored to organize routes and client #106

Merged
merged 3 commits into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,4 @@ output=json\n\


RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g typescript" 2>&1
ENV DEV_ENVIRONMENT=true
17 changes: 17 additions & 0 deletions .github/workflows/bandith_security_scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Source code security scan using Bandit
on:
pull_request:
paths:
- "**/*.py"

jobs:
bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Bandit
run: |
.github/workflows/scripts/run_bandit_scan.sh



9 changes: 9 additions & 0 deletions .github/workflows/scripts/run_bandit_scan.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

# Make sure we are using the latest version
docker pull cytopia/bandit:latest

# Scan source code and only report on high severity issues
docker run --rm -v "$(pwd)":/data cytopia/bandit -r /data -ll
20 changes: 20 additions & 0 deletions api/api_gateway/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from os import environ
from fastapi import FastAPI

from front_end import view
from .routers import ops, organisations, scans

if "DEV_ENVIRONMENT" in environ:
app = FastAPI()
else:
# This is only needed until a dedicated domain name is live
app = FastAPI(root_path="/v1")

app.include_router(ops.router, prefix="/ops", tags=["ops"])
app.include_router(
organisations.router,
prefix="/organisations",
tags=["organisations"],
)
app.include_router(scans.router, prefix="/scans", tags=["scans"])
app.include_router(view.router)
41 changes: 41 additions & 0 deletions api/api_gateway/routers/ops.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from os import environ
from fastapi import APIRouter, Depends
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from database.db import db_session
from logger import log

router = APIRouter()


# Dependency
def get_db():
db = db_session()
try:
yield db
finally:
db.close()


@router.get("/version")
def version():
return {"version": environ.get("GIT_SHA", "unknown")}


def get_db_version(session):

query = "SELECT version_num FROM alembic_version"
full_name = session.execute(query).fetchone()[0]
return full_name


@router.get("/healthcheck")
def healthcheck(session: Session = Depends(get_db)):
try:
full_name = get_db_version(session)
db_status = {"able_to_connect": True, "db_version": full_name}
except SQLAlchemyError as err:
log.error(err)
db_status = {"able_to_connect": False}

return {"database": db_status}
40 changes: 40 additions & 0 deletions api/api_gateway/routers/organisations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from fastapi import APIRouter, Depends, Response, status
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from fastapi.responses import RedirectResponse
from database.db import db_session
from logger import log

from models.Organisation import Organisation
from schemas.Organization import OrganizationCreate

router = APIRouter()


# Dependency
def get_db():
db = db_session()
try:
yield db
finally:
db.close()


# TODO Require auth and redirect to home
# TODO Push errors to cloudwatch metric and response when debug enabled
@router.post("/create", response_class=RedirectResponse)
def create_organisation(
organisation: OrganizationCreate,
response: Response,
session: Session = Depends(get_db),
):

try:
new_organisation = Organisation(name=organisation.name)
session.add(new_organisation)
session.commit()
return RedirectResponse("/dashboard")
except SQLAlchemyError as err:
log.error(err)
response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
return {"error": f"error creating organisation: {err}"}
17 changes: 17 additions & 0 deletions api/api_gateway/routers/scans.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from fastapi import APIRouter
from logger import log
from pydantic import BaseModel
from crawler.crawler import crawl
import uuid

router = APIRouter()


class CrawlUrl(BaseModel):
url: str


@router.post("/crawl")
def crawl_endpoint(crawl_url: CrawlUrl):
log.info(f"Crawling {crawl_url}")
crawl(uuid.uuid4(), crawl_url.url)
76 changes: 0 additions & 76 deletions api/api_gateway/v1/api.py

This file was deleted.

10 changes: 5 additions & 5 deletions api/front_end/view.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from fastapi import Depends, FastAPI, Request, HTTPException
from fastapi import APIRouter, Depends, Request, HTTPException
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from babel.plural import PluralRule
Expand All @@ -12,7 +12,7 @@
import json
import os

app = FastAPI()
router = APIRouter()


# Dependency
Expand Down Expand Up @@ -66,12 +66,12 @@ def plural_formatting(key_value, input, locale):
templates.env.filters["plural_formatting"] = plural_formatting


@app.get("/", response_class=HTMLResponse)
@router.get("/", response_class=HTMLResponse)
async def force_lang():
return RedirectResponse("/en")


@app.get("/{locale}", response_class=HTMLResponse)
@router.get("/{locale}", response_class=HTMLResponse)
async def home(request: Request, locale: str):
try:
if locale not in languages:
Expand All @@ -88,7 +88,7 @@ async def home(request: Request, locale: str):
# TODO Require auth & limit to users current organisation
# TODO Push errors to cloudwatch metric and response when debug enabled
# TODO Enable detailed error messages via debug flag
@app.get("/{locale}/dashboard", response_class=HTMLResponse)
@router.get("/{locale}/dashboard", response_class=HTMLResponse)
async def dashboard(request: Request, locale: str, session: Session = Depends(get_db)):
try:
if locale not in languages:
Expand Down
13 changes: 2 additions & 11 deletions api/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from mangum import Mangum
from api_gateway.v1 import api
from front_end import view
from api_gateway import api
from logger import log
from database.migrate import migrate_head
from storage import storage
Expand All @@ -18,9 +17,7 @@
from models.TemplateScanTrigger import TemplateScanTrigger # noqa: F401
from models.User import User # noqa: F401


api_v1 = api.app
app = view.app
app = api.app


def print_env_variables():
Expand All @@ -35,13 +32,7 @@ def handler(event, context):
if "httpMethod" in event:
# Assume it is an API Gateway event
asgi_handler = Mangum(app)

if "path" in event:
if event["path"].lower().startswith("/api/v1"):
asgi_handler = Mangum(api_v1)

response = asgi_handler(event, context)

return response

elif "Records" in event:
Expand Down
16 changes: 8 additions & 8 deletions api/tests/api_gateway/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,39 @@
from fastapi.testclient import TestClient
from unittest.mock import patch

from api_gateway.v1 import api
from api_gateway import api
from sqlalchemy.exc import SQLAlchemyError

client = TestClient(api.app)


def test_version_with_no_GIT_SHA():
response = client.get("/api/v1/version")
response = client.get("/ops/version")
assert response.status_code == 200
assert response.json() == {"version": "unknown"}


@patch.dict(os.environ, {"GIT_SHA": "foo"}, clear=True)
def test_version_with_GIT_SHA():
response = client.get("/api/v1/version")
response = client.get("/ops/version")
assert response.status_code == 200
assert response.json() == {"version": "foo"}


@patch("api_gateway.v1.api.get_db_version")
@patch("api_gateway.routers.ops.get_db_version")
def test_healthcheck_success(mock_get_db_version):
mock_get_db_version.return_value = "foo"
response = client.get("/api/v1/healthcheck")
response = client.get("/ops/healthcheck")
assert response.status_code == 200
expected_val = {"database": {"able_to_connect": True, "db_version": "foo"}}
assert response.json() == expected_val


@patch("api_gateway.v1.api.get_db_version")
@patch("api_gateway.v1.api.log")
@patch("api_gateway.routers.ops.get_db_version")
@patch("api_gateway.routers.ops.log")
def test_healthcheck_failure(mock_log, mock_get_db_version):
mock_get_db_version.side_effect = SQLAlchemyError()
response = client.get("/api/v1/healthcheck")
response = client.get("/ops/healthcheck")
assert response.status_code == 200
expected_val = {"database": {"able_to_connect": False}}
assert response.json() == expected_val
Expand Down
2 changes: 1 addition & 1 deletion api/tests/front_end/test_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from front_end import view

client = TestClient(view.app)
client = TestClient(view.router)


def test_langing_page_redirect_to_en():
Expand Down
2 changes: 1 addition & 1 deletion bin/api_create_organisation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
echo hitting create organisation endpoint
curl "http://api:8080/2015-03-31/functions/function/invocations" -d '{
"resource": "/",
"path": "/api/v1/organisation",
"path": "/organisation",
"requestContext": {},
"httpMethod": "POST",
"headers": {},
Expand Down
2 changes: 1 addition & 1 deletion bin/api_healthcheck.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
echo hitting api healthcheck endpoint
curl "http://api:8080/2015-03-31/functions/function/invocations" -d '{
"resource": "/",
"path": "/api/v1/healthcheck",
"path": "/healthcheck",
"requestContext": {},
"httpMethod": "GET",
"headers": {},
Expand Down
2 changes: 1 addition & 1 deletion bin/api_version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
echo hitting api version endpoint
curl "http://api:8080/2015-03-31/functions/function/invocations" -d '{
"resource": "/",
"path": "/api/v1/version",
"path": "/version",
"requestContext": {},
"httpMethod": "GET",
"headers": {},
Expand Down