Skip to content

Commit

Permalink
feat(swagger): add Swagger full integration
Browse files Browse the repository at this point in the history
add security and Swagger models to all web services.
  • Loading branch information
Rubenrod18 committed Sep 20, 2020
2 parents 7f87738 + a32fb17 commit 1eaf8d8
Show file tree
Hide file tree
Showing 22 changed files with 391 additions and 480 deletions.
7 changes: 4 additions & 3 deletions app/__init__.py
Expand Up @@ -6,13 +6,13 @@

from app import extensions
from app.blueprints import blueprints
from app.middleware import middleware
from app.middleware import Middleware


def create_app(env_config: str) -> Flask:
app = flask.Flask(__name__)
app.config.from_object(env_config)
app.wsgi_app = middleware(app)
app.wsgi_app = Middleware(app)

init_logging(app)
init_app(app)
Expand All @@ -22,7 +22,8 @@ def create_app(env_config: str) -> Flask:


def init_app(app: Flask) -> None:
"""Call the method 'init_app' to register the extensions in the flask.Flask object passed as parameter."""
"""Call the method 'init_app' to register the extensions in the flask.Flask
object passed as parameter."""
extensions.init_app(app)


Expand Down
2 changes: 1 addition & 1 deletion app/blueprints/__init__.py
Expand Up @@ -12,4 +12,4 @@
blueprint_auth,
blueprint_documents,
blueprint_tasks,
]
]
88 changes: 27 additions & 61 deletions app/blueprints/auth.py
Expand Up @@ -5,40 +5,36 @@
from flask_restx import Resource
from flask_security import verify_password
from flask_security.passwordless import generate_login_token
from werkzeug.exceptions import Forbidden, Unauthorized, UnprocessableEntity, NotFound
from werkzeug.exceptions import (Forbidden, Unauthorized, UnprocessableEntity,
NotFound)

from app.extensions import api as root_api
from app.models.user import User as UserModel, user_datastore
from app.celery.tasks import reset_password_email
from app.utils.cerberus_schema import MyValidator, user_login_schema, confirm_reset_password_schema
from app.utils.cerberus_schema import (MyValidator, user_login_schema,
confirm_reset_password_schema)
from app.utils.decorators import token_required
from config import Config
from app.utils.swagger_models.auth import (AUTH_LOGIN_SW_MODEL,
AUTH_TOKEN_SW_MODEL,
AUTH_REQUEST_RESET_PASSWORD_SW_MODEL,
AUTH_RESET_PASSWORD_SW_MODEL)

blueprint = Blueprint('auth', __name__)
api = root_api.namespace('auth', description='Authentication endpoints')

logger = logging.getLogger(__name__)


@api.route('/login')
class AuthUserLoginResource(Resource):
_parser = api.parser()
_parser.add_argument('email', type=str, location='json')
_parser.add_argument('password', type=str, location='json')

@api.doc(responses={
200: 'Success',
401: 'Unauthorized',
403: 'Forbidden',
422: 'Unprocessable Entity',
})
@api.expect(_parser)
@api.doc(responses={401: 'Unauthorized', 403: 'Forbidden',
422: 'Unprocessable Entity'})
@api.expect(AUTH_LOGIN_SW_MODEL)
@api.marshal_with(AUTH_TOKEN_SW_MODEL)
def post(self) -> tuple:
data = request.get_json()

v = MyValidator(schema=user_login_schema())
v.allow_unknown = False

if not v.validate(data):
raise UnprocessableEntity(v.errors)

Expand All @@ -53,41 +49,23 @@ def post(self) -> tuple:
token = generate_login_token(user)
# TODO: Pending to testing whats happen id add a new field in user model when a user is logged
flask_security.login_user(user)
return {
'token': token,
}, 200
return {'token': f'Bearer {token}'}, 200


@api.route('/logout')
class AuthUserLogoutResource(Resource):
_parser = api.parser()
_parser.add_argument(Config.SECURITY_TOKEN_AUTHENTICATION_HEADER, location='headers', required=True,
default='Bearer token')

@api.doc(responses={
200: 'Success',
401: 'Unauthorized',
})
@api.expect(_parser)
@api.doc(responses={200: 'Success', 401: 'Unauthorized'},
security='auth_token')
@token_required
def post(self) -> tuple:
flask_security.logout_user()

return {}, 200


# TODO: update endpoint name
@api.route('/reset_password')
class RequestResetPasswordResource(Resource):
_parser = api.parser()
_parser.add_argument('email', type=str, location='json')

@api.doc(responses={
200: 'Success', # TODO: change status code to 202
403: 'Forbidden',
404: 'Not Found',
})
@api.expect(_parser)
@api.doc(responses={202: 'Success', 403: 'Forbidden', 404: 'Not Found'})
@api.expect(AUTH_REQUEST_RESET_PASSWORD_SW_MODEL)
def post(self) -> tuple:
data = request.get_json()
email = data.get('email')
Expand All @@ -103,27 +81,22 @@ def post(self) -> tuple:
raise Forbidden('User is not active')

token = user.get_reset_token()

reset_password_url = url_for('auth_reset_password_resource', token=token, _external=True)
reset_password_url = url_for('auth_reset_password_resource',
token=token,
_external=True)

email_data = {
'email': user.email,
'reset_password_url': reset_password_url,
}

reset_password_email.delay(email_data)

return {}, 200
return {}, 202


# TODO: update endpoint name
@api.route('/reset_password/<token>')
@api.doc(params={'token': 'A password reset token created previously'})
class ResetPasswordResource(Resource):
@api.doc(responses={
200: 'Success',
403: 'Forbidden',
})
@api.doc(responses={200: 'Success', 403: 'Forbidden'})
def get(self, token: str) -> tuple:
user = UserModel.verify_reset_token(token)

Expand All @@ -138,15 +111,10 @@ def get(self, token: str) -> tuple:

return {}, 200

_parser = api.parser()
_parser.add_argument('password', type=str, location='json')

@api.doc(responses={
200: 'Success',
403: 'Forbidden',
422: 'Unprocessable Entity',
})
@api.expect(_parser)
@api.doc(responses={200: 'Success', 403: 'Forbidden',
422: 'Unprocessable Entity'})
@api.expect(AUTH_RESET_PASSWORD_SW_MODEL)
@api.marshal_with(AUTH_TOKEN_SW_MODEL)
def post(self, token: str) -> tuple:
data = request.get_json()

Expand All @@ -164,6 +132,4 @@ def post(self, token: str) -> tuple:

token = generate_login_token(user)

return {
'token': token,
}, 200
return {'token': f'Bearer {token}'}, 200
9 changes: 5 additions & 4 deletions app/blueprints/base.py
@@ -1,6 +1,6 @@
import logging

from flask_restx import Resource, fields
from flask_restx import Resource
from flask import Blueprint
from peewee import ModelSelect
from werkzeug.exceptions import UnprocessableEntity
Expand All @@ -11,11 +11,11 @@

blueprint = Blueprint('base', __name__)
api = root_api.namespace('', description='Base endpoints')

logger = logging.getLogger(__name__)


class BaseResource(Resource):
db_model: db.Model
db_model = db.Model
request_validation_schema = {}

def request_validation(self, request_data: dict) -> None:
Expand All @@ -27,7 +27,8 @@ def request_validation(self, request_data: dict) -> None:
def get_request_query_fields(self, request_data: dict) -> tuple:
return get_request_query_fields(self.db_model, request_data)

def create_search_query(self, query: ModelSelect, request_data: dict) -> ModelSelect:
def create_search_query(self, query: ModelSelect, request_data: dict) \
-> ModelSelect:
return create_search_query(self.db_model, query, request_data)


Expand Down

0 comments on commit 1eaf8d8

Please sign in to comment.