Skip to content

Commit

Permalink
Fix: Conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
himanshu-wedensday committed Mar 13, 2024
2 parents fc99553 + d478b01 commit bb21a53
Show file tree
Hide file tree
Showing 36 changed files with 151 additions and 156 deletions.
3 changes: 2 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[flake8]
ignore = E501
ignore = E501, W503
max-line-length = 120
exclude =
.git,
__pycache__,
venv/
env/
python-fastapi
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
### Checklist

- [ ] PR description included
- [ ] `yarn test` passes
- [ ] `pytest` passes
- [ ] Tests are [changed or added]
- [ ] Relevant documentation is changed or added (and PR referenced)

Expand Down
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: FastAPI-CI

on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
branches:
- main

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- uses: actions/setup-python@v4
with:
python-version: 3.11.8

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Install plugin
run: pip install pytest-github-actions-annotate-failures

- name: Build coverage file
run: |
pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=app app/tests/ | tee pytest-coverage.txt
- name: Pytest coverage comment
uses: MishaKav/pytest-coverage-comment@main
with:
pytest-coverage-path: ./pytest-coverage.txt
junitxml-path: ./pytest.xml

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ venv

*__pycache__*

# Test files
*.coverage

# Temp files
tmp/
temp/
Expand Down
7 changes: 3 additions & 4 deletions alembic/versions/45f7c66353f4_initialize_all_models.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
"""initialize all models
Revision ID: 45f7c66353f4
Revises:
Revises:
Create Date: 2023-10-16 11:46:50.753722
"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from app import models # noqa
from app.models import users # noqa
from app.models import users # noqa

# revision identifiers, used by Alembic.
revision: str = "45f7c66353f4"
Expand Down
8 changes: 2 additions & 6 deletions app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@
from app.middlewares.request_id_injection import RequestIdInjection

from app.routes import api_router
from app.utils.exception_handler import (
exception_handler,
validation_exception_handler,
http_exception_handler
)

from app.utils.exception_handler import exception_handler, validation_exception_handler, http_exception_handler


app = FastAPI(
Expand All @@ -38,6 +33,7 @@
app.add_middleware(RateLimitMiddleware)
app.add_middleware(RequestIdInjection)
app.add_middleware(CacheMiddleware, cached_endpoints=cached_endpoints.CACHED_ENDPOINTS)

# Include the routers
app.include_router(api_router, prefix="/api")

Expand Down
9 changes: 6 additions & 3 deletions app/celery_tasks/tasks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from celery import shared_task
import time

from celery import shared_task


@shared_task
def add(x, y):
time.sleep(20)
return x + y
time.sleep(10)
return x + y
2 changes: 1 addition & 1 deletion app/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
__all__ = [
"engine",
"get_redis_pool",
]
]
15 changes: 9 additions & 6 deletions app/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class CelerySettings(BaseSettings):
class Config:
env_file = ".config.celery"


class DBSettings(BaseSettings):
DB_HOSTNAME: str
DB_PORT: str
Expand All @@ -25,16 +26,18 @@ class Settings(BaseSettings):
SECRET_KEY: str
REDIS_URL: str
SLACK_WEBHOOK_URL: str
ALLOWED_HOSTS: list = ['*']
CACHE_MAX_AGE:int =60
ALLOWED_HOSTS: list = ["*"]
CACHE_MAX_AGE: int = 60

class Config:
env_file = ".env"


class CachedEndpoints(BaseSettings):
CACHED_ENDPOINTS:list=[
'/cache-sample/'
]
CACHED_ENDPOINTS: list = ["/cache-sample/"]


db_settings = DBSettings()
settings = Settings()
celery_settings = CelerySettings()
cached_endpoints=CachedEndpoints()
cached_endpoints = CachedEndpoints()
4 changes: 1 addition & 3 deletions app/config/celery_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import os
from functools import lru_cache
from kombu import Queue

from app.config.base import settings

Expand All @@ -24,4 +22,4 @@ def get_settings():
return config_cls()


celery_settings = get_settings()
celery_settings = get_settings()
14 changes: 5 additions & 9 deletions app/config/celery_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

def create_celery():
celery_app = current_celery_app
celery_app.config_from_object(settings, namespace='CELERY')
celery_app.config_from_object(settings, namespace="CELERY")
celery_app.conf.update(task_track_started=True)
celery_app.conf.update(task_serializer='pickle')
celery_app.conf.update(result_serializer='pickle')
celery_app.conf.update(accept_content=['pickle', 'json'])
celery_app.conf.update(task_serializer="pickle")
celery_app.conf.update(result_serializer="pickle")
celery_app.conf.update(accept_content=["pickle", "json"])
celery_app.conf.update(result_expires=celery_settings.RESULT_EXPIRES)
celery_app.conf.update(result_persistent=celery_settings.RESULT_PERSISTENT)
celery_app.conf.update(worker_send_task_events=celery_settings.WORKER_SEND_TASK_EVENT)
Expand All @@ -24,9 +24,5 @@ def get_task_info(task_id):
return task info for the given task_id
"""
task_result = AsyncResult(task_id)
result = {
"task_id": task_id,
"task_status": task_result.status,
"task_result": task_result.result
}
result = {"task_id": task_id, "task_status": task_result.status, "task_result": task_result.result}
return result
1 change: 1 addition & 0 deletions app/config/redis_config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .base import settings
from redis import asyncio


async def get_redis_pool():
return asyncio.from_url(settings.REDIS_URL, encoding="utf-8", decode_responses=True)
14 changes: 0 additions & 14 deletions app/daos/home.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
import asyncio
import random

from fastapi.exceptions import HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from app.middlewares.rate_limiter_middleware import RateLimitMiddleware
from app.middlewares.request_id_injection import (
RequestIdInjection,
request_id_contextvar
)
from pybreaker import CircuitBreakerError
from dependencies import circuit_breaker
from app.utils.slack_notification_utils import send_slack_message


async def external_service_call():
# Simulate network delay
Expand All @@ -25,4 +12,3 @@ async def external_service_call():
raise Exception("External service failed")

return "Success from external service"

3 changes: 1 addition & 2 deletions app/daos/users.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import json
import pickle

from redis import Redis
Expand All @@ -8,12 +7,12 @@
from app.constants.messages.users import user_messages as messages
from app.models import User
from app.schemas.users.users_request import CreateUser, Login
from app.schemas.users.users_response import UserOutResponse
from werkzeug.security import check_password_hash
from fastapi_pagination.ext.sqlalchemy import paginate
from sqlalchemy import select
from app.utils.user_utils import check_existing_field, responseFormatter


def get_user(user_id: int, dbSession: Session):
try:
cache_key = f"user:{user_id}"
Expand Down
29 changes: 11 additions & 18 deletions app/middlewares/cache_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@

from app.wrappers.cache_wrappers import create_cache, retrieve_cache


class CacheMiddleware(BaseHTTPMiddleware):
def __init__(
self,
app,
cached_endpoints: List[str],
self,
app,
cached_endpoints: List[str],
):
super().__init__(app)
self.cached_endpoints = cached_endpoints

def matches_any_path(self, path_url):
for pattern in self.cached_endpoints:
if pattern in path_url:
Expand All @@ -25,43 +27,34 @@ def matches_any_path(self, path_url):
async def dispatch(self, request: Request, call_next) -> Response:
path_url = request.url.path
request_type = request.method
cache_control = request.headers.get('Cache-Control', None)
auth = request.headers.get('Authorization', "token public")
cache_control = request.headers.get("Cache-Control", None)
auth = request.headers.get("Authorization", "token public")
token = auth.split(" ")[1]
max_age=settings.CACHE_MAX_AGE
max_age = settings.CACHE_MAX_AGE
key = f"{path_url}_{token}"
#checking if end point has been added into cached endpoint
matches = self.matches_any_path(path_url)

#check if endpoint method is get if not call next function or middleware in chain
if request_type != 'GET':
return await call_next(request)

#getting stored cache for endpoint
stored_cache = await retrieve_cache(key)
res = stored_cache and cache_control != 'no-cache'
res = stored_cache and cache_control != "no-cache"

if not res:
response: Response = await call_next(request)
response_body = [chunk async for chunk in response.body_iterator]
response.body_iterator = iterate_in_threadpool(iter(response_body))
#checking if response is success
if response.status_code == 200:
#check if cache header is no-strore if true it return (Applicable for cached endpoint as well as cache control header in request)
if cache_control == 'no-store':
return response
#check if cache control has max has param if true create a cache for endpoint
elif cache_control and "max-age" in cache_control:
max_age = int(cache_control.split("=")[1])
await create_cache(response_body[0].decode(), key, max_age)
# no cache-control header is present but endpoint is in cached endpoint so by default it is caching response for 60 second
elif matches:
await create_cache(response_body[0].decode(), key, max_age)
return response

else:
# If the response is cached, return it directly
headers = {
'Cache-Control': f"max-age:{stored_cache[1]}"
}
return StreamingResponse(iter([stored_cache[0]]), media_type="application/json", headers=headers)
headers = {"Cache-Control": f"max-age:{stored_cache[1]}"}
return StreamingResponse(iter([stored_cache[0]]), media_type="application/json", headers=headers)
5 changes: 0 additions & 5 deletions app/middlewares/rate_limiter_middleware.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import os
import datetime

from app.config.redis_config import get_redis_pool
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
Expand All @@ -14,7 +10,6 @@
class RateLimitMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
client_ip = request.client.host
now = datetime.datetime.now()

redis = await get_redis_pool()
try:
Expand Down
7 changes: 4 additions & 3 deletions app/middlewares/request_id_injection.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from fastapi import FastAPI
import contextvars
import uuid

from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from fastapi.responses import JSONResponse
import contextvars
import uuid

request_id_contextvar = contextvars.ContextVar("request_id", default=None)


class RequestIdInjection(BaseHTTPMiddleware):
def dispatch(self, request: Request, call_next):
request_id = str(uuid.uuid4())
Expand Down
2 changes: 1 addition & 1 deletion app/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

from .users import User

__all__ = ["User"]
__all__ = ["User"]
3 changes: 1 addition & 2 deletions app/models/users.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from sqlalchemy import Column
from sqlalchemy import Column, event
from sqlalchemy.sql import func
from sqlalchemy.sql.sqltypes import DateTime
from sqlalchemy.sql.sqltypes import Integer
from sqlalchemy.sql.sqltypes import String
from sqlalchemy.ext.declarative import declarative_base
from werkzeug.security import generate_password_hash
from sqlalchemy import event

from app.sessions.db import engine

Expand Down
1 change: 1 addition & 0 deletions app/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .users import user_router
from .celery_router import celery_sample_router
from .cache_router import cache_sample_router

api_router = APIRouter()
api_router.include_router(user_router, prefix="/user")
api_router.include_router(home_router, prefix="/home")
Expand Down
Loading

0 comments on commit bb21a53

Please sign in to comment.