Compare commits

...

2 Commits

Author SHA1 Message Date
f259aa3993 Work on swag documentation 2024-03-13 13:43:34 +01:00
0f7d69899a Added flasgger requirement 2024-03-13 13:43:16 +01:00
14 changed files with 117 additions and 56 deletions

View File

@ -2,13 +2,13 @@ from flask import Flask
from flask_jwt_extended import JWTManager from flask_jwt_extended import JWTManager
from flask_mail import Mail from flask_mail import Mail
from flasgger import Swagger from flasgger import Swagger
from flask_cors import CORS
from app.doc.main_swag import main_swagger
app = Flask(__name__) app = Flask(__name__)
jwt_manager = JWTManager(app) jwt_manager = JWTManager(app)
mail = Mail(app) mail = Mail(app)
swag = Swagger(app) swag = Swagger(app, template=main_swagger)
cors = CORS(app)
def create_app(): def create_app():
from app.api import bp, bp_errors, bp_product, bp_user, bp_cart from app.api import bp, bp_errors, bp_product, bp_user, bp_cart

View File

@ -1,7 +1,7 @@
from flask import jsonify, abort, request from flask import jsonify, abort, request
from flask_jwt_extended import jwt_required, get_jwt_identity from flask_jwt_extended import jwt_required, get_jwt_identity
from app.doc.cart import show_cart_swagger, add_to_cart_swagger from app.doc.cart_swag import show_cart_swagger, add_to_cart_swagger
from flasgger import swag_from from flasgger import swag_from

View File

@ -1,12 +1,11 @@
from flask import jsonify, abort from flask import jsonify, abort
from app.doc.main import main_swagger
from flasgger import swag_from from flasgger import swag_from
from app.doc.root_swag import root_swagger
from app.api import bp from app.api import bp
@bp.route('/') @bp.route('/')
@swag_from(main_swagger) @swag_from(root_swagger)
def hello(): def hello():
return jsonify({'message': 'Hello, Flask!'}) return jsonify({'message': 'Hello, Flask!'})

View File

@ -1,7 +1,7 @@
from flask import jsonify, abort, request from flask import jsonify, abort, request
from flask_jwt_extended import jwt_required, get_jwt_identity from flask_jwt_extended import jwt_required, get_jwt_identity
from app.doc.product import get_products_swagger from app.doc.product_swag import get_products_swagger
from flasgger import swag_from from flasgger import swag_from

View File

@ -2,6 +2,10 @@ from app.api import bp_user
from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt
from flask import request, abort from flask import request, abort
from flasgger import swag_from
from app.doc.user_swag import login_swagger
from app.services.user_service import UserService from app.services.user_service import UserService
@bp_user.route('/register', methods=['POST']) @bp_user.route('/register', methods=['POST'])
@ -19,6 +23,7 @@ def register():
return result, status_code return result, status_code
@bp_user.route('/login', methods=['POST']) @bp_user.route('/login', methods=['POST'])
@swag_from(login_swagger)
def login(): def login():
username = request.json.get('username') username = request.json.get('username')
password = request.json.get('password') password = request.json.get('password')

View File

@ -1,13 +1,8 @@
show_cart_swagger = { show_cart_swagger = {
"tags": ["Cart"], "tags": ["Cart"],
"parameters": "security":
[ [
{ {"JWT": []}
"name": "Authorization",
"in": "header",
"type": "string",
"required": True
}
], ],
"responses": "responses":
{ {
@ -45,24 +40,25 @@ show_cart_swagger = {
add_to_cart_swagger ={ add_to_cart_swagger ={
"tags": ["Cart"], "tags": ["Cart"],
"security":
[
{"JWT": []}
],
"parameters": "parameters":
[ [
{
"name": "Authorization",
"in": "header",
"type": "string",
"required": True
},
{ {
"name": "product_id", "name": "product_id",
"description": "ID of product to add to cart.",
"in": "path", "in": "path",
"type": "int", "type": "int",
"required": True
}, },
{ {
"name": "count", "name": "count",
"description": "Count of the products. If not provided, defaults to 1",
"in": "query", "in": "query",
"type": "int", "type": "int",
"default": 1,
"minimum": 1,
"required": False "required": False
} }
], ],
@ -74,7 +70,7 @@ add_to_cart_swagger ={
}, },
"400": "400":
{ {
"description": "Incorrect usage. For example id of product not found or product count < 1" "description": "Causes:\n- Count is < 1"
} }
} }
} }

18
app/doc/main_swag.py Normal file
View File

@ -0,0 +1,18 @@
main_swagger = {
"info": {
"title": "Shop API",
"version": "0.1",
"description": "Simple shop API using flask and co.\nFeatures include:\n- Not working\n- Successful registration of users\n- Adding items to cart\n- I don't know",
},
"host": "localhost:1236",
"schemes": "http",
"securityDefinitions": {
"JWT": {
"type": "apiKey",
"scheme": "bearer",
"name": "Authorization",
"in": "header",
"description": "JWT Authorization header using the Bearer scheme.\n*Make sure to prefix the token with **Bearer**!*"
}
}
}

View File

@ -1,23 +0,0 @@
get_products_swagger = {
"paths": {
"/get": {
"get": {
"summary": "Get products",
"responses": {
"200": {
"description": "Successfully retrieved products",
"schema": {
"type": "object",
"properties": {
"products": {"type": "array", "items": {"type": "object", "properties": {"id": {"type": "int"}, "name": {"type": "string"}, "price": {"type": "float"}}}}
}
}
},
"400": {
"description": "Bad request"
}
}
}
}
}
}

21
app/doc/product_swag.py Normal file
View File

@ -0,0 +1,21 @@
get_products_swagger = {
"methods": ["GET"],
"tags": ["Products"],
"parameters": [
],
"responses":
{
"200":
{
"description": "Get a page of products",
"schema":
{
"type": "object",
"properties": {
"message": {"type": "string", "example": "Hello, Flask!"}
}
}
}
}
}

View File

@ -1,8 +1,5 @@
main_swagger = { root_swagger = {
"methods": ["GET"], "methods": ["GET"],
"parameters": [
],
"responses": "responses":
{ {
"200": "200":
@ -11,7 +8,8 @@ main_swagger = {
"schema": "schema":
{ {
"type": "object", "type": "object",
"properties": { "properties":
{
"message": {"type": "string", "example": "Hello, Flask!"} "message": {"type": "string", "example": "Hello, Flask!"}
} }
} }

View File

45
app/doc/user_swag.py Normal file
View File

@ -0,0 +1,45 @@
login_swagger = {
"methods": ["POST"],
"tags": ["User"],
"description": "Logs in using username and password and returns a JWT token for further authorization of requests.\n**The token is valid for 1 hour**",
"parameters":
[
{
"in": "body",
"name": "body",
"description": "Username and password payload",
"required": True,
"schema":
{
"type": "object",
"properties":
{
"username": {"type": "string", "example": "mycoolusername"},
"password": {"type": "string", "example": "MyStrongPassword123"}
}
}
}
],
"responses":
{
"200":
{
"description": "Returns a fresh token",
"schema":
{
"type": "object",
"properties": {
"token": {"type": "string", "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTcxMDMyMjkyOCwianRpIjoiZDFhYzQxZDktZjA4NC00MmYzLThlMWUtZWFmZjJiNGU1MDAyIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6MjMwMDEsIm5iZiI6MTcxMDMyMjkyOCwiZXhwIjoxNzEwMzI2NTI4fQ.SW7LAi1j5vDOEIvzeN-sy0eHPP9PFJFkXYY029O35w0"}
}
}
},
"400":
{
"description": "Possible causes:\n- Missing username or password from request.\n- Nonexistent username"
},
"401":
{
"description": "Password is incorrect"
}
}
}

View File

@ -23,17 +23,18 @@ class CartService:
cursor.execute("select count from cart_item where cart_id = %s and product_id = %s", (user_id, product_id)) cursor.execute("select count from cart_item where cart_id = %s and product_id = %s", (user_id, product_id))
result = cursor.fetchone() result = cursor.fetchone()
if cursor.rowcount != 0: if cursor.rowcount == 1:
cursor.execute("update cart_item set count = count + %s where cart_id = %s and product_id = %s", (count, user_id, product_id)) cursor.execute("update cart_item set count = count + %s where cart_id = %s and product_id = %s", (count, user_id, product_id))
else: else:
cursor.execute("insert into cart_item(cart_id, product_id, count) values (%s, %s, %s)", (user_id, product_id, count)) cursor.execute("insert into cart_item(cart_id, product_id, count) values (%s, %s, %s)", (user_id, product_id, count))
db_connection.commit() db_connection.commit()
return {"Success": "Successfully added to cart"}, 200
except Error as e: except Error as e:
return {"Failed": f"Failed to add item to cart. Reason: {e}"}, 400 return {"Failed": f"Failed to add item to cart. Reason: {e}"}, 500
return {"Success": "Successfully added to cart"}, 200
@staticmethod @staticmethod
def update_count(user_id: str, product_id: int, count: int) -> Tuple[Union[dict, str], int]: def update_count(user_id: str, product_id: int, count: int) -> Tuple[Union[dict, str], int]:

View File

@ -3,7 +3,8 @@ gunicorn==20.1.0
mysql-connector-python==8.3.0 mysql-connector-python==8.3.0
python-dotenv==1.0.1 python-dotenv==1.0.1
Flask-JWT-Extended==4.5.3 Flask-JWT-Extended==4.5.3
PyJWT==2.8.0 flasgger==0.9.7.1
Flask-Mail==0.9.1 Flask-Mail==0.9.1
PyJWT==2.8.0
redis==4.5.4 redis==4.5.4
bcrypt==4.1.2 bcrypt==4.1.2