From f7fe90b185372b6f0c2192340c9994cd6e7381a5 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 6 Jul 2023 11:33:13 +0100 Subject: [PATCH] Major project restructure and setup --- backend/.env.example | 1 + backend/README.md | 59 +++++++++++++++++-- backend/app/__init__.py | 0 backend/app/config.py | 15 +++++ backend/app/dependencies.py | 14 +++++ backend/app/main.py | 8 +++ backend/app/portfolio/__init__.py | 0 backend/app/portfolio/router.py | 18 ++++++ backend/main.py | 26 -------- .../base.txt} | 0 10 files changed, 111 insertions(+), 30 deletions(-) create mode 100644 backend/.env.example create mode 100644 backend/app/__init__.py create mode 100644 backend/app/config.py create mode 100644 backend/app/dependencies.py create mode 100644 backend/app/main.py create mode 100644 backend/app/portfolio/__init__.py create mode 100644 backend/app/portfolio/router.py delete mode 100644 backend/main.py rename backend/{requirements.txt => requirements/base.txt} (100%) diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 00000000..2be99ae8 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1 @@ +API_KEY = example-api-key \ No newline at end of file diff --git a/backend/README.md b/backend/README.md index 61d0663e..90567d47 100644 --- a/backend/README.md +++ b/backend/README.md @@ -4,14 +4,65 @@ learning and data modelling services. # Usage -## Local +## Prerequisites +Python 3.8+ +Poetry for managing project dependencies and virtual environment. -For running the serice locally, natigate to the backend directory and run the following command: +## Installation and setup +1. Clone this directory and navigate into the project directory. -```bash -uvicorn main:app --reload +```commandline +git clone https://github.com/Hestia-Homes/Model.git +cd backend ``` +2. For environment management, I'm using conda with pycharm which is a convenient setup for development +on a mac M1 however using tools such as poetry or pipenv is also fine. + +For example, to install conda and create a virtual environment for this project, run the following commands: + +```commandline +conda create -n backend python=3.10 +conda activate backend +``` + +then enter the virtual environment and install the dependencies using conda. + +```commandline +conda install --file requirements/base.txt +``` + +3. Duplicate .env.example and rename it to .env +```commandline +cp .env.example .env +``` + +4. Open .env and fill in the required environment variables. + +## Running the Application + +from within the application you can run with the following command: + +```commandline +uvicorn app.main:app --reload +``` + +You application will be available at the designated url + +## API Documentation + +FastAPI automatically generates interactive API documentation for your application. To access the docs, start your +server and visit /docs in your browser. Alternatively, you can go to +/redoc to view the documentation in the ReDoc format. + +## Testing +To run tests, run the following command from the root of the project directory: + +```commandline +pytest +``` + + ### Thoughts for authenticating the frontend with the backend To provide secure communication between your frontend Next.js application and your backend FastAPI service, you have several options. Here are a few popular approaches: diff --git a/backend/app/__init__.py b/backend/app/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/app/config.py b/backend/app/config.py new file mode 100644 index 00000000..3bcb14d0 --- /dev/null +++ b/backend/app/config.py @@ -0,0 +1,15 @@ +from functools import lru_cache +from pydantic import BaseSettings + + +class Settings(BaseSettings): + API_KEY: str + API_KEY_NAME: str = "X-API-KEY" + + class Config: + env_file = ".env" + + +@lru_cache() +def get_settings(): + return Settings() diff --git a/backend/app/dependencies.py b/backend/app/dependencies.py new file mode 100644 index 00000000..4002cded --- /dev/null +++ b/backend/app/dependencies.py @@ -0,0 +1,14 @@ +from fastapi import Depends, HTTPException, status +from fastapi.security import APIKeyHeader +from app.config import get_settings + + +api_key_header = APIKeyHeader(name=get_settings().API_KEY_NAME, auto_error=False) + + +async def validate_api_key(api_key_header: str = Depends(api_key_header)): + if api_key_header != get_settings().API_KEY: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, detail="Could not validate credentials" + ) + return api_key_header diff --git a/backend/app/main.py b/backend/app/main.py new file mode 100644 index 00000000..8e36c9ad --- /dev/null +++ b/backend/app/main.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI +from app.portfolio import router as portfolio_router + + +app = FastAPI() + + +app.include_router(portfolio_router.router) diff --git a/backend/app/portfolio/__init__.py b/backend/app/portfolio/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/app/portfolio/router.py b/backend/app/portfolio/router.py new file mode 100644 index 00000000..27f32f7a --- /dev/null +++ b/backend/app/portfolio/router.py @@ -0,0 +1,18 @@ +from fastapi import APIRouter, Depends +from app.dependencies import validate_api_key + +router = APIRouter( + prefix="/portfolio", + dependencies=[Depends(validate_api_key)], + tags=["portfolio"], + responses={404: {"description": "Not found"}} +) + + +@router.get("/{portfolio_id}") +async def get_portfolio(portfolio_id: int): + return { + "portfolio_id": portfolio_id, + "name": "My Portfolio", + "description": "This is my portfolio", + } diff --git a/backend/main.py b/backend/main.py deleted file mode 100644 index 4f536437..00000000 --- a/backend/main.py +++ /dev/null @@ -1,26 +0,0 @@ -from fastapi import FastAPI, Depends, HTTPException, status -from fastapi.security import APIKeyHeader - -API_KEY = "example-api-key" -API_KEY_NAME = "X-API-KEY" - -api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False) - -app = FastAPI() - - -async def validate_api_key(api_key_header: str = Depends(api_key_header)): - if api_key_header != API_KEY: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, detail="Could not validate credentials" - ) - return api_key_header - - -@app.get("/portfolio/{portfolio_id}", dependencies=[Depends(validate_api_key)]) -async def get_portfolio(portfolio_id: int): - return { - "portfolio_id": portfolio_id, - "name": "My Portfolio", - "description": "This is my portfolio", - } diff --git a/backend/requirements.txt b/backend/requirements/base.txt similarity index 100% rename from backend/requirements.txt rename to backend/requirements/base.txt