Skip to content
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
70 changes: 25 additions & 45 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from dotenv import load_dotenv
from flasgger import Swagger
from sqlalchemy import MetaData
from flask_smorest import Api

Expand Down Expand Up @@ -41,6 +40,31 @@ def register_blueprints():
app.config["API_VERSION"] = "v1"
app.config["OPENAPI_VERSION"] = "3.0.2"

# flask-smorest openapi swagger
app.config["OPENAPI_URL_PREFIX"] = "/"
app.config["OPENAPI_SWAGGER_UI_PATH"] = "/"
app.config["OPENAPI_SWAGGER_UI_URL"] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist/"

# flask-smorest Swagger UI top level authorize dialog box
app.config["API_SPEC_OPTIONS"] = {
"components": {
"securitySchemes": {
"access_token": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "Enter your JWT access token",
},
"refresh_token": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "Enter your JWT refresh token",
},
}
}
}

# PostgreSQL-compatible naming convention (to follow the naming convention already used in the DB)
# https://stackoverflow.com/questions/4107915/postgresql-default-constraint-names
naming_convention = {
Expand Down Expand Up @@ -73,47 +97,3 @@ def invalid_token_callback(error):
@jwt.unauthorized_loader
def missing_token_callback(error):
return jsonify(code="authorization_required", error="JWT needed for this operation. Login, if needed."), 401


swagger_config = {
'openapi': '3.0.0',
'title': 'Ecommerce REST API',
'version': None,
'termsOfService': None,
'description': None,
'specs': [
{
"endpoint": 'api_spec',
"route": '/api_spec.json',
"rule_filter": lambda rule: True, # all in
"model_filter": lambda tag: True, # all in
}
],
'components': {
'securitySchemes': {
'access_token': {
'type': 'http',
'scheme': 'bearer',
'bearerFormat': 'JWT',
'description': 'Enter your JWT access token'
},
'refresh_token': {
'type': 'http',
'scheme': 'bearer',
'bearerFormat': 'JWT',
'description': 'Enter your JWT refresh token'
}
}
},
'specs_route': '/'
}

template = {
'tags': [
{'name': 'Category', 'description': 'Operations with categories'},
{'name': 'Subcategory', 'description': 'Operations with subategories'},
{'name': 'Product', 'description': 'Operations with products'},
{'name': 'User', 'description': 'Operations with users'},
]
}
swagger = Swagger(app, template=template, config=swagger_config, merge=True)
91 changes: 7 additions & 84 deletions app/routes/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,46 +14,15 @@
from app.models import User
from app.schemas import AuthIn, AuthOut

bp = Blueprint("auth", __name__)
bp = Blueprint("Auth", __name__)


@bp.route("/register")
class Register(MethodView):
@bp.doc(summary="Register a new user")
@bp.arguments(AuthIn)
@bp.response(201)
def post(self, data):
"""
Register a new user.
---
tags:
- User
description: Register a new user.
requestBody:
required: true
description: email - Email id <br> password - Password
content:
application/json:
schema:
type: object
required:
- email
- password
properties:
email:
type: string
password:
type: string
responses:
201:
description: User registered successfully.
400:
description: Invalid input.
409:
description: Email already exists.
500:
description: Internal Server Error.
"""

user = User()
user.set_password(data["password"])

Expand All @@ -74,42 +43,10 @@ def post(self, data):

@bp.route("/login")
class Login(MethodView):
"""Login a user and return access & refresh tokens."""

@bp.doc(summary="Login a user")
@bp.arguments(AuthIn)
@bp.response(200, AuthOut)
def post(self, data):
"""
Login a user.
---
tags:
- User
description: Login a user.
requestBody:
required: true
description: email - Email id <br> password - Password
content:
application/json:
schema:
type: object
required:
- email
- password
properties:
email:
type: string
password:
type: string
responses:
200:
description: User logged in successfully.
400:
description: Invalid input.
401:
description: Invalid email or password.
500:
description: Internal Server Error.
"""
user = User.get(email=data["email"])
if not user or not user.check_password(data["password"]):
return abort(
Expand All @@ -129,26 +66,12 @@ def post(self, data):

@bp.route("/refresh")
class Refresh(MethodView):
"""Get new access token using your refresh token."""

@jwt_required(refresh=True)
@bp.doc(
summary="Get new access token using your refresh token",
security=[{"refresh_token": []}],
)
@bp.response(200, AuthOut(only=("access_token",)))
def post(self):
"""
Get new access token using your refresh token
---
tags:
- User
description: Get new access token using your refresh token.
security:
- refresh_token: []
responses:
200:
description: New access token.
401:
description: Token expired, missing or invalid.
500:
description: Internal Server Error.
"""
identity = get_jwt_identity()
return {"access_token": create_access_token(identity=identity, fresh=False)}
Loading