initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
./vscode
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"editor.lineNumbers": "off",
|
||||
"editor.padding.top": 3,
|
||||
"editor.padding.bottom": 3,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.fontSize": 14,
|
||||
"editor.lineHeight": 4.5,
|
||||
"editor.suggestFontSize": 15,
|
||||
// "editor.suggestLineHeight": 4,
|
||||
"breadcrumbs.enabled": false,
|
||||
"workbench.tips.enabled": false,
|
||||
"workbench.statusBar.visible": false,
|
||||
// "workbench.editor.showTabs": "single",
|
||||
"git.enableSmartCommit": true,
|
||||
"workbench.editor.editorActionsLocation": "hidden",
|
||||
// "workbench.activityBar.location": "hidden",
|
||||
"workbench.editor.enablePreviewFromQuickOpen": false,
|
||||
"editor.lightbulb.enabled": "off",
|
||||
"editor.selectionHighlight": false,
|
||||
"editor.overviewRulerBorder": false,
|
||||
"editor.renderLineHighlight": "none",
|
||||
"editor.occurrencesHighlight": "off"
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# Use Python base image
|
||||
FROM python:3.10
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy files to container
|
||||
COPY . /app
|
||||
|
||||
# Install dependencies
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Expose port 5000
|
||||
EXPOSE 5000
|
||||
|
||||
# Set environment variables
|
||||
ENV FLASK_APP=app.py
|
||||
ENV FLASK_RUN_HOST=0.0.0.0
|
||||
|
||||
# Run the application
|
||||
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "wsgi:wsgi_app"]
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
from app import create_app
|
||||
|
||||
app = create_app()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||
@@ -0,0 +1,27 @@
|
||||
from flask import Flask
|
||||
from flask_cors import CORS
|
||||
from app.config import Config
|
||||
from app.routes import auth_bp, eligibility_bp
|
||||
from app.errors import method_not_allowed, unsupported_media_type
|
||||
|
||||
|
||||
def create_app():
|
||||
"""Factory function to create a Flask app instance"""
|
||||
app = Flask(__name__)
|
||||
|
||||
# Load configuration
|
||||
app.config.from_object(Config)
|
||||
|
||||
CORS(app)
|
||||
|
||||
# Register blueprints
|
||||
app.register_blueprint(auth_bp)
|
||||
# app.register_blueprint(loan_bp)
|
||||
app.register_blueprint(eligibility_bp, url_prefix="/eligibility")
|
||||
# app.register_blueprint(repayment_bp)
|
||||
|
||||
# Error Handlers
|
||||
app.register_error_handler(405, method_not_allowed)
|
||||
app.register_error_handler(415, unsupported_media_type)
|
||||
|
||||
return app
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
import os
|
||||
|
||||
class Config:
|
||||
"""Base configuration for Flask app"""
|
||||
|
||||
SECRET_KEY = os.getenv("SECRET_KEY", "supersecretkey")
|
||||
API_BASE_URL = "https://coreapi.dev.simbrellang.net/v1/api/salary"
|
||||
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", "your_jwt_secret")
|
||||
DEBUG = True
|
||||
@@ -0,0 +1 @@
|
||||
from .handlers import method_not_allowed, unsupported_media_type
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
from flask import jsonify
|
||||
from app.helpers.response_helper import ResponseHelper
|
||||
|
||||
|
||||
def method_not_allowed(error):
|
||||
return ResponseHelper.method_not_allowed(message="Method Not Allowed")
|
||||
|
||||
|
||||
def not_found(error):
|
||||
return ResponseHelper.not_found(message="URL Not Found")
|
||||
|
||||
|
||||
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)
|
||||
Binary file not shown.
@@ -0,0 +1,251 @@
|
||||
from flask import jsonify
|
||||
from typing import List, Dict, Union, Optional, Any
|
||||
|
||||
|
||||
class ResponseHelper:
|
||||
"""
|
||||
A helper class for building standardized JSON responses in Flask.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def build_response(
|
||||
status: bool,
|
||||
message: str,
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
status_code: int = 200,
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Build a standardized JSON response.
|
||||
|
||||
Args:
|
||||
status (bool): Indicates whether the request was successful.
|
||||
message (str): A message describing the result of the request.
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
status_code (int): The HTTP status code for the response.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
response = {
|
||||
"status": status,
|
||||
"statusCode": status_code,
|
||||
"message": message,
|
||||
"data": data if data is not None else {},
|
||||
"error": error if error is not None else {},
|
||||
}
|
||||
return jsonify(response), status_code
|
||||
|
||||
@staticmethod
|
||||
def success(
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
message: str = "Successful",
|
||||
status_code: int = 200,
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return a success response.
|
||||
|
||||
Args:
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
message (str): A message describing the result of the request.
|
||||
status_code (int): The HTTP status code for the response.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(True, message, data, status_code, error)
|
||||
|
||||
@staticmethod
|
||||
def error(
|
||||
message: str = "An error occurred",
|
||||
status_code: int = 400,
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return an error response.
|
||||
|
||||
Args:
|
||||
message (str): A message describing the error.
|
||||
status_code (int): The HTTP status code for the response.
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(False, message, data, status_code, error)
|
||||
|
||||
@staticmethod
|
||||
def created(
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
message: str = "Resource created successfully",
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return a response for a created resource.
|
||||
|
||||
Args:
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
message (str): A message describing the result of the request.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(True, message, data, 201, error)
|
||||
|
||||
@staticmethod
|
||||
def updated(
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
message: str = "Resource updated successfully",
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return a response for an updated resource.
|
||||
|
||||
Args:
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
message (str): A message describing the result of the request.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(True, message, data, 200, error)
|
||||
|
||||
@staticmethod
|
||||
def internal_server_error(
|
||||
message: str = "Internal Server Error",
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return a response for an internal server error.
|
||||
|
||||
Args:
|
||||
message (str): A message describing the error.
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(False, message, data, 500, error)
|
||||
|
||||
@staticmethod
|
||||
def unauthorized(
|
||||
message: str = "Unauthorized",
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return a response for an unauthorized request.
|
||||
|
||||
Args:
|
||||
message (str): A message describing the error.
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(False, message, data, 401, error)
|
||||
|
||||
@staticmethod
|
||||
def forbidden(
|
||||
message: str = "Forbidden",
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return a response for a forbidden request.
|
||||
|
||||
Args:
|
||||
message (str): A message describing the error.
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(False, message, data, 403, error)
|
||||
|
||||
@staticmethod
|
||||
def not_found(
|
||||
message: str = "Resource not found",
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return a response for a not found resource.
|
||||
|
||||
Args:
|
||||
message (str): A message describing the error.
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(False, message, data, 404, error)
|
||||
|
||||
@staticmethod
|
||||
def unprocessable_entity(
|
||||
message: str = "Unprocessable entity",
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return a response for an unprocessable entity.
|
||||
|
||||
Args:
|
||||
message (str): A message describing the error.
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(False, message, data, 422, error)
|
||||
|
||||
@staticmethod
|
||||
def method_not_allowed(
|
||||
message: str = "Method Not Allowed",
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return a response for a method not allowed error.
|
||||
|
||||
Args:
|
||||
message (str): A message describing the error.
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(False, message, data, 405, error)
|
||||
|
||||
@staticmethod
|
||||
def bad_request(
|
||||
message: str = "Bad Request",
|
||||
data: Optional[Union[Dict, List, str]] = None,
|
||||
error: Optional[Union[Dict, str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Return a response for a bad request error.
|
||||
|
||||
Args:
|
||||
message (str): A message describing the error.
|
||||
data (Optional[Union[Dict, List, str]]): The data to return in the response.
|
||||
error (Optional[Union[Dict, str]]): Any error details to include in the response.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: A dictionary representing the JSON response.
|
||||
"""
|
||||
return ResponseHelper.build_response(False, message, data, 400, error)
|
||||
@@ -0,0 +1,4 @@
|
||||
from .authentication import auth_bp
|
||||
from .eligibility import eligibility_bp
|
||||
# from .loan import loan_bp
|
||||
# from .repayment import repayment_bp
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
import requests
|
||||
from app.utils.auth import get_headers
|
||||
|
||||
auth_bp = Blueprint("auth", __name__)
|
||||
|
||||
@auth_bp.route("/health", methods=["GET"])
|
||||
def health():
|
||||
return jsonify({"status": "Up"})
|
||||
|
||||
@auth_bp.route("/login", methods=["POST"])
|
||||
def login():
|
||||
data = request.json
|
||||
api_url = "https://coreapi.dev.simbrellang.net/api/auth/login"
|
||||
|
||||
response = requests.post(api_url, json=data)
|
||||
if response.status_code == 200:
|
||||
return jsonify(response.json()), 200
|
||||
return jsonify({"error": "Invalid credentials"}), response.status_code
|
||||
@@ -0,0 +1,42 @@
|
||||
from flask import Blueprint, request, jsonify, current_app
|
||||
import requests
|
||||
from app.utils.auth import get_headers
|
||||
|
||||
eligibility_bp = Blueprint("eligibility", __name__)
|
||||
|
||||
|
||||
@eligibility_bp.route("/check", methods=["POST"])
|
||||
def eligibility_check():
|
||||
data = request.json
|
||||
api_url = f"{current_app.config['API_BASE_URL']}/EligibilityCheck"
|
||||
|
||||
print(api_url)
|
||||
|
||||
# response = requests.post(api_url, json=data, headers=get_headers())
|
||||
# return jsonify(response.json()), response.status_code
|
||||
response = {
|
||||
"customerId": "CN621868",
|
||||
"transactionId": "Tr201712RK9232P115",
|
||||
"countryCode": "NGR",
|
||||
"msisdn": "2348012345678",
|
||||
"eligibleOffers": [
|
||||
{
|
||||
"offerId": 101,
|
||||
"minAmount": 5000,
|
||||
"maxAmount": 20000,
|
||||
"productId": 2030,
|
||||
"tenor": 30,
|
||||
},
|
||||
{
|
||||
"offerId": 102,
|
||||
"minAmount": 20000,
|
||||
"maxAmount": 50000,
|
||||
"productId": 2090,
|
||||
"tenor": 90,
|
||||
},
|
||||
],
|
||||
"resultCode": "00",
|
||||
"resultDescription": "Successful",
|
||||
}
|
||||
|
||||
return jsonify(response), 200
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
import requests
|
||||
from flask import current_app
|
||||
|
||||
def get_headers():
|
||||
return {
|
||||
"Authorization": f"Bearer {current_app.config['JWT_SECRET_KEY']}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import logging
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||
handlers=[
|
||||
# logging.StreamHandler(), # Log to console
|
||||
logging.FileHandler("app.log", mode='a') # Log to file
|
||||
]
|
||||
)
|
||||
|
||||
logger = logging.getLogger("DetectionService")
|
||||
@@ -0,0 +1,30 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
flask:
|
||||
build: .
|
||||
ports:
|
||||
- "5000:5000"
|
||||
environment:
|
||||
- FLASK_APP=app.py
|
||||
- FLASK_RUN_HOST=0.0.0.0
|
||||
volumes:
|
||||
- .:/app
|
||||
restart: always
|
||||
networks:
|
||||
- digital
|
||||
|
||||
swagger:
|
||||
image: swaggerapi/swagger-ui
|
||||
ports:
|
||||
- "9000:8080"
|
||||
volumes:
|
||||
- ./openapi.yml:/usr/local/openapi.yml
|
||||
environment:
|
||||
- SWAGGER_JSON=/usr/local/openapi.yml
|
||||
restart: always
|
||||
networks:
|
||||
- digital
|
||||
networks:
|
||||
digital:
|
||||
driver: bridge
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Sample Flask API
|
||||
description: A simple Flask API with Swagger documentation running in Docker
|
||||
version: 1.0.0
|
||||
contact:
|
||||
name: API Support
|
||||
email: support@example.com
|
||||
license:
|
||||
name: MIT
|
||||
url: https://opensource.org/licenses/MIT
|
||||
|
||||
servers:
|
||||
- url: http://localhost:5000
|
||||
description: Local development server
|
||||
|
||||
paths:
|
||||
/health:
|
||||
get:
|
||||
summary: Returns a health message
|
||||
responses:
|
||||
200:
|
||||
description: A successful response
|
||||
/eligibility/check:
|
||||
post:
|
||||
summary: Performs eligibility check on a user
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
transactionId:
|
||||
type: string
|
||||
description: The transaction ID
|
||||
example: Tr201712RK9232P115
|
||||
customerId:
|
||||
type: string
|
||||
description: The customer ID
|
||||
example: CN621868
|
||||
countryCode:
|
||||
type: string
|
||||
description: The country code
|
||||
example: NGR
|
||||
accountId:
|
||||
type: string
|
||||
description: The account ID
|
||||
example: ACN8263457
|
||||
msisdn:
|
||||
type: string
|
||||
description: The MSISDN
|
||||
example: 8012345678
|
||||
channel:
|
||||
type: string
|
||||
description: The channel
|
||||
example: 100
|
||||
responses:
|
||||
200:
|
||||
description: A successful response
|
||||
@@ -0,0 +1,7 @@
|
||||
# Flask and Extensions
|
||||
Flask==2.3.3
|
||||
Flask-Marshmallow==0.15.0
|
||||
marshmallow==3.19.0
|
||||
Flask-Cors==3.0.10
|
||||
gunicorn
|
||||
requests
|
||||
Reference in New Issue
Block a user