From 45d25581cdc973702e22ee79bf986b419234baf5 Mon Sep 17 00:00:00 2001 From: Mohamed Selim Date: Sun, 6 Aug 2023 02:48:30 +0300 Subject: [PATCH] Migrate from Flask to FastAPI with redis --- inventory-service/Dockerfile | 2 +- inventory-service/app.py | 179 ++++++++++++++++++++--------------- 2 files changed, 106 insertions(+), 75 deletions(-) diff --git a/inventory-service/Dockerfile b/inventory-service/Dockerfile index c4e8551..8c0460e 100644 --- a/inventory-service/Dockerfile +++ b/inventory-service/Dockerfile @@ -17,4 +17,4 @@ COPY . . EXPOSE 7000 # Run the application -CMD ["python", "app.py"] +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7000"] diff --git a/inventory-service/app.py b/inventory-service/app.py index d7f82a5..39c09db 100644 --- a/inventory-service/app.py +++ b/inventory-service/app.py @@ -1,38 +1,73 @@ -from flask import Flask, jsonify, request -from flask_sqlalchemy import SQLAlchemy -from flask_caching import Cache -import lorem -import random - -app = Flask(__name__) -app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:password@db2:5432/inventory_db' -app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False - -# Configure Redis caching -app.config['CACHE_TYPE'] = 'redis' -app.config['CACHE_REDIS_URL'] = 'redis://caching-db:6379/0' -cache = Cache(app) -cache.init_app(app) - -db = SQLAlchemy(app) - - -class Product(db.Model): - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(100), unique=True) - price = db.Column(db.Float) - description = db.Column(db.String(500)) - - def __init__(self, name, price, description): - self.name = name - self.price = price - self.description = description - - -@cache.cached() -@app.route('/products', methods=['GET']) -def get_all_products(): - products = Product.query.all() +from fastapi import FastAPI, Query, Depends, HTTPException +from sqlalchemy import create_engine, Column, Float, Integer, String +from sqlalchemy.orm import sessionmaker, declarative_base +from aioredis import create_redis_pool +import random, lorem, json + +app = FastAPI() + +# Database configuration +database_url = 'postgresql://postgres:password@db2:5432/inventory_db' +engine = create_engine(database_url) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() + +# Redis caching configuration +redis_url = 'redis://caching-db:6379/0' +redis = None + +# Dependency to get a database session +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +# Product model +class Product(Base): + __tablename__ = 'products' + + id = Column(Integer, primary_key=True) + name = Column(String(100), unique=True) + price = Column(Float) + description = Column(String(500)) + +# Create database tables +Base.metadata.create_all(bind=engine) + +# Add dummy product data +db = SessionLocal() +for i in range(1, 101): + name = f'Product_{i}' + price = round(random.uniform(1.99, 999.99), 2) + description = lorem.get_sentence(count=2) + product = Product(name=name, price=price, description=description) + db.add(product) +db.commit() +db.close() + +# Set up Redis connection +@app.on_event("startup") +async def startup_event(): + global redis + redis = await create_redis_pool(redis_url) + +# Shutdown Redis connection +@app.on_event("shutdown") +async def shutdown_event(): + global redis + redis.close() + await redis.wait_closed() + +# API endpoints +@app.get('/products') +async def get_all_products(db = Depends(get_db)): + products = await redis.get('products') + if products: + return json.loads(products) + + products = db.query(Product).all() result = [] for product in products: result.append({ @@ -40,27 +75,17 @@ def get_all_products(): 'price': product.price, 'description': product.description }) - return jsonify(result) + await redis.set('products', json.dumps(result)) + return result -@cache.cached(timeout=50) -@app.route('/products/', methods=['GET']) -def get_product_by_name(name): - product = Product.query.filter_by(name=name).first() - if product: - return jsonify({ - 'name': product.name, - 'price': product.price, - 'description': product.description - }) - else: - return jsonify({'message': 'Product not found'}), 404 - +@app.get('/products/search') +async def search_product(keyword: str = Query(..., min_length=1), db = Depends(get_db)): + result = await redis.get(keyword) + if result: + return json.loads(result) -@app.route('/products/search', methods=['GET']) -def search_product(): - keyword = request.args.get('keyword') - products = Product.query.filter(Product.name.ilike(f'%{keyword}%')).all() + products = db.query(Product).filter(Product.name.ilike(f'%{keyword}%')).all() result = [] for product in products: result.append({ @@ -68,14 +93,17 @@ def search_product(): 'price': product.price, 'description': product.description }) - return jsonify(result) + await redis.set(keyword, json.dumps(result)) + return result -@app.route('/products/price-range', methods=['GET']) -def get_products_by_price_range(): - min_price = float(request.args.get('min_price')) - max_price = float(request.args.get('max_price')) - products = Product.query.filter(Product.price.between(min_price, max_price)).all() +@app.get('/products/price-range') +async def get_products_by_price_range(min_price: float = Query(...), max_price: float = Query(...), db = Depends(get_db)): + result = await redis.get(f'{min_price}_{max_price}') + if result: + return json.loads(result) + + products = db.query(Product).filter(Product.price.between(min_price, max_price)).all() result = [] for product in products: result.append({ @@ -83,21 +111,24 @@ def get_products_by_price_range(): 'price': product.price, 'description': product.description }) - return jsonify(result) - + await redis.set(f'{min_price}_{max_price}', json.dumps(result)) -if __name__ == '__main__': - # Create database tables - with app.app_context(): - db.create_all() + return result - # Add dummy product data - for i in range(1, 101): - name = f'Product {i}' - price = round(random.uniform(1.99, 999.99), 2) - description = lorem.get_sentence(count=2) - product = Product(name, price, description) - db.session.add(product) - db.session.commit() +@app.get('/products/{name}') +async def get_product_by_name(name: str, db = Depends(get_db)): + product = await redis.get(name) + if product: + return json.loads(product) - app.run(host='0.0.0.0', port=7000) + product = db.query(Product).filter_by(name=name).first() + if product: + result = { + 'name': product.name, + 'price': product.price, + 'description': product.description + } + await redis.set(name, json.dumps(result)) + return result + else: + raise HTTPException(status_code=404, detail='Product not found') \ No newline at end of file