diff --git a/app.log b/app.log index a1fc158..cc06c06 100644 --- a/app.log +++ b/app.log @@ -132,3 +132,7 @@ TypeError: require_api_key() missing 1 required positional argument: 'f' 2025-03-21 09:41:16,993 - INFO - Processing Disbursement request 2025-03-21 09:41:16,995 - ERROR - Validation Error: {'requestId': ['Missing data for required field.'], 'productId': ['Missing data for required field.'], 'collectAmountMgtFee': ['Missing data for required field.'], 'collectAmountVAT': ['Missing data for required field.'], 'provideAmount': ['Missing data for required field.'], 'countryId': ['Missing data for required field.'], 'debtId': ['Missing data for required field.'], 'collectAmountInsurance': ['Missing data for required field.'], 'channel': ['Unknown field.'], 'lienAmount': ['Unknown field.'], '$type': ['Unknown field.'], 'msisdn': ['Unknown field.'], 'countryCode': ['Unknown field.']} 2025-03-21 09:48:19,845 - ERROR - Unauthorized access: Missing API key. +2025-03-21 10:32:19,196 - ERROR - Unauthorized access: Missing API key. +2025-03-21 10:46:47,209 - ERROR - Unauthorized access: Missing API key. +2025-03-21 11:05:50,287 - INFO - Processing Disbursement request +2025-03-21 11:05:50,290 - ERROR - Validation Error: {'debtId': ['Missing data for required field.'], 'requestId': ['Missing data for required field.'], 'productId': ['Missing data for required field.'], 'provideAmount': ['Missing data for required field.'], 'collectAmountInsurance': ['Missing data for required field.'], 'collectAmountVAT': ['Missing data for required field.'], 'countryId': ['Missing data for required field.'], 'collectAmountMgtFee': ['Missing data for required field.'], 'msisdn': ['Unknown field.'], 'channel': ['Unknown field.'], 'lienAmount': ['Unknown field.'], 'countryCode': ['Unknown field.'], '$type': ['Unknown field.']} diff --git a/app/__init__.py b/app/__init__.py index 2897066..2bf7f5d 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -2,7 +2,7 @@ from flask import Flask from flask_cors import CORS from app.config import Config from app.routes import api -from app.errors import bad_request, method_not_allowed, not_found +from app.errors import method_not_allowed, unsupported_media_type def create_app(): """ Factory function to create a Flask app instance """ @@ -19,8 +19,7 @@ def create_app(): # Error Handlers - app.register_error_handler(400, bad_request) - app.register_error_handler(404, not_found) app.register_error_handler(405, method_not_allowed) + app.register_error_handler(415, unsupported_media_type) return app diff --git a/app/errors/__init__.py b/app/errors/__init__.py index 77d4ccc..15c380c 100644 --- a/app/errors/__init__.py +++ b/app/errors/__init__.py @@ -1 +1 @@ -from .handlers import bad_request, method_not_allowed, not_found \ No newline at end of file +from .handlers import method_not_allowed, unsupported_media_type \ No newline at end of file diff --git a/app/errors/handlers.py b/app/errors/handlers.py index 74c4608..8f7a945 100644 --- a/app/errors/handlers.py +++ b/app/errors/handlers.py @@ -9,3 +9,6 @@ def not_found(error): def bad_request(error): return ResponseHelper.bad_request(message="Bad Request") + +def unsupported_media_type(error): + return ResponseHelper.error(message="Unsupported Media Type", status_code=415) diff --git a/app/middlewares/__init__.py b/app/middlewares/__init__.py index 8a0d8bb..d9c542e 100644 --- a/app/middlewares/__init__.py +++ b/app/middlewares/__init__.py @@ -1,2 +1,4 @@ from .verify_api_key import require_api_key -from .app_id_checker import require_app_id \ No newline at end of file +from .app_id_checker import require_app_id +from .request_validator import validate_json +from .cors import enforce_json \ No newline at end of file diff --git a/app/middlewares/cors.py b/app/middlewares/cors.py index e655fac..4e87ed7 100644 --- a/app/middlewares/cors.py +++ b/app/middlewares/cors.py @@ -1,9 +1,8 @@ -# app/middlewares/cors.py from flask import request +from app.helpers.response_helper import ResponseHelper -def cors_headers(response): - """Allow cross-origin requests""" - response.headers["Access-Control-Allow-Origin"] = "*" - response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, PATCH, DELETE" - response.headers["Access-Control-Allow-Headers"] = "Authorization, Content-Type" - return response + +def enforce_json(): + """Middleware to enforce JSON Content-Type for incoming requests""" + if request.method in ["POST", "PUT", "PATCH"] and request.content_type != "application/json": + return ResponseHelper.error( message="Content-Type must be application/json") diff --git a/app/middlewares/request_validator.py b/app/middlewares/request_validator.py index 903e450..a5f2dbc 100644 --- a/app/middlewares/request_validator.py +++ b/app/middlewares/request_validator.py @@ -1,11 +1,16 @@ -# app/middlewares/request_validator.py +from functools import wraps from flask import request from app.helpers.response_helper import ResponseHelper +from app.utils.logger import logger -def validate_json(): - """Ensure request has valid JSON""" - if not request.is_json: - return ResponseHelper.error( - message="Request must be JSON", - status_code=415 - ) +def validate_json(f): + """Decorator to ensure the request has a valid JSON body.""" + @wraps(f) + def decorated_function(*args, **kwargs): + if not request.is_json: + logger.error("Invalid request: Request must be JSON.") + return ResponseHelper.error(message="Request must be JSON", status_code=415) + + return f(*args, **kwargs) + + return decorated_function diff --git a/app/routes/routes.py b/app/routes/routes.py index 202fa37..6b60be4 100644 --- a/app/routes/routes.py +++ b/app/routes/routes.py @@ -11,18 +11,17 @@ from app.blueprints import ( NewTransactionCheckService, ) from app.utils.logger import logger -from app.middlewares import require_api_key -from app.middlewares import require_app_id +from app.middlewares import require_api_key, require_app_id, enforce_json api = Blueprint("api", __name__) -# @api.before_request -# def require_api_key_middleware(): -# """Middleware applied globally to all API routes in this blueprint""" -# return require_api_key() +@api.before_request +def cors_middleware(): + """Middleware applied globally to all API routes in this blueprint""" + return enforce_json() @@ -41,6 +40,7 @@ def rac_check(): @require_api_key @require_app_id def disbursement(): + data = request.get_json() # logger.info(f"Disbursement request received: {data}") response = DisbursementService.process_request(data)