diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..66e035c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,47 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +venv/ +.venv/ +env/ +ENV/ +*.egg-info/ +.installed.cfg +*.egg + +# Git +.git +.gitignore + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# Environment variables +.env +.env.local + +# Logs and databases +*.log +logs/ +*.sqlite3 +*.db + +# JMeter (only exclude binary files, keep the test plan) +jmeter/bin/ +jmeter/lib/ + +# Dockerfile +Dockerfile +docker-compose.yaml +.dockerignore + +# Testing +.coverage +htmlcov/ +.pytest_cache/ \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 1485401..2112eb8 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,7 +5,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -46,6 +85,7 @@ "node.js.selected.package.eslint": "(autodetect)", "node.js.selected.package.tslint": "(autodetect)", "nodejs_package_manager_path": "npm", + "settings.editor.selected.configurable": "preference.NewJMeterHomeConfigurable", "vue.rearranger.settings.migration": "true" } } @@ -93,6 +133,9 @@ + + + diff --git a/Docker b/Docker deleted file mode 100644 index 5f453b1..0000000 --- a/Docker +++ /dev/null @@ -1,21 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /app - -# Install dependencies -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -# Copy application code -COPY . . - -# Set environment variables -ENV PYTHONDONTWRITEBYTECODE=1 \ - PYTHONUNBUFFERED=1 \ - PORT=5000 - -# Expose port -EXPOSE 5000 - -# Run the application with Gunicorn -CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:create_app()"] \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4ee6a94 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +FROM python:3.11-slim + +WORKDIR /app + +# Install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY . . + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PORT=8080 + +# Create non-root user for security +RUN adduser --disabled-password --gecos "" appuser && \ + chown -R appuser:appuser /app + +USER appuser + +# Expose port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:8080/v1/api/salary/health || exit 1 + +# Run the application with Gunicorn - use wsgi.py instead +CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "4", "wsgi:create_app()"] \ No newline at end of file diff --git a/README.md b/README.md index cf08687..4a42b8e 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,20 @@ This project implements the Simbrella FirstAdvance API as defined in the OpenAPI The easiest way to run the application is using Docker Compose: ```bash -# Build and start the containers +# Stop any running containers +docker-compose down + +# Rebuild the image +docker-compose build + +# Start the container docker-compose up -d -# View logs -docker-compose logs -f +# Check if the container is running +docker ps -# Stop the containers -docker-compose down +# Check the logs for any errors +docker-compose logs ``` ## Manual Setup diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..b2dbd8a --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,3 @@ +""" +App package initialization. +""" \ No newline at end of file diff --git a/api/__pycache__/middleware.cpython-313.pyc b/app/__pycache__/middleware.cpython-313.pyc similarity index 100% rename from api/__pycache__/middleware.cpython-313.pyc rename to app/__pycache__/middleware.cpython-313.pyc diff --git a/api/__pycache__/models.cpython-313.pyc b/app/__pycache__/models.cpython-313.pyc similarity index 100% rename from api/__pycache__/models.cpython-313.pyc rename to app/__pycache__/models.cpython-313.pyc diff --git a/api/__pycache__/routes.cpython-313.pyc b/app/__pycache__/routes.cpython-313.pyc similarity index 100% rename from api/__pycache__/routes.cpython-313.pyc rename to app/__pycache__/routes.cpython-313.pyc diff --git a/app/controllers/__init__.py b/app/controllers/__init__.py new file mode 100644 index 0000000..019094c --- /dev/null +++ b/app/controllers/__init__.py @@ -0,0 +1,3 @@ +""" +Controllers package initialization. +""" \ No newline at end of file diff --git a/api/controllers/__pycache__/collection.cpython-313.pyc b/app/controllers/__pycache__/collection.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/collection.cpython-313.pyc rename to app/controllers/__pycache__/collection.cpython-313.pyc diff --git a/api/controllers/__pycache__/consent.cpython-313.pyc b/app/controllers/__pycache__/consent.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/consent.cpython-313.pyc rename to app/controllers/__pycache__/consent.cpython-313.pyc diff --git a/api/controllers/__pycache__/disbursement.cpython-313.pyc b/app/controllers/__pycache__/disbursement.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/disbursement.cpython-313.pyc rename to app/controllers/__pycache__/disbursement.cpython-313.pyc diff --git a/api/controllers/__pycache__/eligibility.cpython-313.pyc b/app/controllers/__pycache__/eligibility.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/eligibility.cpython-313.pyc rename to app/controllers/__pycache__/eligibility.cpython-313.pyc diff --git a/api/controllers/__pycache__/health.cpython-313.pyc b/app/controllers/__pycache__/health.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/health.cpython-313.pyc rename to app/controllers/__pycache__/health.cpython-313.pyc diff --git a/api/controllers/__pycache__/lien.cpython-313.pyc b/app/controllers/__pycache__/lien.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/lien.cpython-313.pyc rename to app/controllers/__pycache__/lien.cpython-313.pyc diff --git a/api/controllers/__pycache__/loan.cpython-313.pyc b/app/controllers/__pycache__/loan.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/loan.cpython-313.pyc rename to app/controllers/__pycache__/loan.cpython-313.pyc diff --git a/api/controllers/__pycache__/notification.cpython-313.pyc b/app/controllers/__pycache__/notification.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/notification.cpython-313.pyc rename to app/controllers/__pycache__/notification.cpython-313.pyc diff --git a/api/controllers/__pycache__/offers.cpython-313.pyc b/app/controllers/__pycache__/offers.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/offers.cpython-313.pyc rename to app/controllers/__pycache__/offers.cpython-313.pyc diff --git a/api/controllers/__pycache__/penal.cpython-313.pyc b/app/controllers/__pycache__/penal.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/penal.cpython-313.pyc rename to app/controllers/__pycache__/penal.cpython-313.pyc diff --git a/api/controllers/__pycache__/rac.cpython-313.pyc b/app/controllers/__pycache__/rac.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/rac.cpython-313.pyc rename to app/controllers/__pycache__/rac.cpython-313.pyc diff --git a/api/controllers/__pycache__/repayment.cpython-313.pyc b/app/controllers/__pycache__/repayment.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/repayment.cpython-313.pyc rename to app/controllers/__pycache__/repayment.cpython-313.pyc diff --git a/api/controllers/__pycache__/sms.cpython-313.pyc b/app/controllers/__pycache__/sms.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/sms.cpython-313.pyc rename to app/controllers/__pycache__/sms.cpython-313.pyc diff --git a/api/controllers/__pycache__/token.cpython-313.pyc b/app/controllers/__pycache__/token.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/token.cpython-313.pyc rename to app/controllers/__pycache__/token.cpython-313.pyc diff --git a/api/controllers/__pycache__/transaction.cpython-313.pyc b/app/controllers/__pycache__/transaction.cpython-313.pyc similarity index 100% rename from api/controllers/__pycache__/transaction.cpython-313.pyc rename to app/controllers/__pycache__/transaction.cpython-313.pyc diff --git a/api/controllers/collection.py b/app/controllers/collection.py similarity index 95% rename from api/controllers/collection.py rename to app/controllers/collection.py index 743afc1..1335f2e 100644 --- a/api/controllers/collection.py +++ b/app/controllers/collection.py @@ -2,8 +2,8 @@ Controller for loan collection endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import api_key_required -from api.models import CollectLoanRequest, CollectLoanResponse +from app.middleware import api_key_required +from app.models import CollectLoanRequest, CollectLoanResponse import logging # Configure logger diff --git a/api/controllers/consent.py b/app/controllers/consent.py similarity index 97% rename from api/controllers/consent.py rename to app/controllers/consent.py index 27d8d2e..367130d 100644 --- a/api/controllers/consent.py +++ b/app/controllers/consent.py @@ -2,8 +2,8 @@ Controller for customer consent endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import basic_auth_required, api_key_required -from api.models import ( +from app.middleware import basic_auth_required, api_key_required +from app.models import ( CustomerConsentRequest, CustomerConsentResponse, RevokeEnableConsentRequest, RevokeEnableConsentResponse ) diff --git a/api/controllers/disbursement.py b/app/controllers/disbursement.py similarity index 95% rename from api/controllers/disbursement.py rename to app/controllers/disbursement.py index 6dbc8b2..f75d9d6 100644 --- a/api/controllers/disbursement.py +++ b/app/controllers/disbursement.py @@ -2,8 +2,8 @@ Controller for loan disbursement endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import api_key_required -from api.models import DisbursementRequest, DisbursementResponse +from app.middleware import api_key_required +from app.models import DisbursementRequest, DisbursementResponse import logging # Configure logger diff --git a/api/controllers/eligibility.py b/app/controllers/eligibility.py similarity index 93% rename from api/controllers/eligibility.py rename to app/controllers/eligibility.py index 20e3ac2..df475d6 100644 --- a/api/controllers/eligibility.py +++ b/app/controllers/eligibility.py @@ -3,8 +3,8 @@ Controller for eligibility check endpoints. """ from flask import Blueprint, request, jsonify, current_app from flask.typing import ResponseReturnValue -from api.middleware import basic_auth_required -from api.models import EligibilityCheckRequest, EligibilityCheckResponse, EligibleOffer +from app.middleware import basic_auth_required +from app.models import EligibilityCheckRequest, EligibilityCheckResponse, EligibleOffer import logging from typing import Dict, Any, List @@ -49,7 +49,7 @@ def eligibility_check() -> ResponseReturnValue: # Create request model req = EligibilityCheckRequest.from_dict(data) - # Process eligibility check (this would connect to the business logic) + # Process eligibility check (this would connect to your business logic) # For demonstration, we'll return a mock response # Create sample offers diff --git a/api/controllers/health.py b/app/controllers/health.py similarity index 86% rename from api/controllers/health.py rename to app/controllers/health.py index 8a1c690..43a665a 100644 --- a/api/controllers/health.py +++ b/app/controllers/health.py @@ -11,13 +11,12 @@ logger = logging.getLogger(__name__) # Create blueprint health_bp = Blueprint('health', __name__) - @health_bp.route('/health', methods=['GET']) def health_check() -> ResponseReturnValue: """ Endpoint to check API health. - This endpoint is used by Docker healthcheck and monitoring systems. + This endpoint is used by Dockerfile healthcheck and monitoring systems. Returns: JSON response with health status diff --git a/api/controllers/lien.py b/app/controllers/lien.py similarity index 94% rename from api/controllers/lien.py rename to app/controllers/lien.py index dae3ca0..a121859 100644 --- a/api/controllers/lien.py +++ b/app/controllers/lien.py @@ -2,8 +2,8 @@ Controller for lien check endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import api_key_required -from api.models import LienCheckRequest, LienCheckResponse +from app.middleware import api_key_required +from app.models import LienCheckRequest, LienCheckResponse import logging # Configure logger diff --git a/api/controllers/loan.py b/app/controllers/loan.py similarity index 98% rename from api/controllers/loan.py rename to app/controllers/loan.py index 7a7d57e..ae5f45b 100644 --- a/api/controllers/loan.py +++ b/app/controllers/loan.py @@ -2,8 +2,8 @@ Controller for loan-related endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import basic_auth_required -from api.models import ( +from app.middleware import basic_auth_required +from app.models import ( ProvideLoanRequest, ProvideLoanResponse, LoanInformationRequest, LoanInformationResponse, Loan ) diff --git a/api/controllers/notification.py b/app/controllers/notification.py similarity index 94% rename from api/controllers/notification.py rename to app/controllers/notification.py index 4e812c1..0d9d388 100644 --- a/api/controllers/notification.py +++ b/app/controllers/notification.py @@ -2,8 +2,8 @@ Controller for notification callback endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import basic_auth_required -from api.models import NotificationCallbackRequest, NotificationCallbackResponse +from app.middleware import basic_auth_required +from app.models import NotificationCallbackRequest, NotificationCallbackResponse import logging # Configure logger diff --git a/api/controllers/offers.py b/app/controllers/offers.py similarity index 96% rename from api/controllers/offers.py rename to app/controllers/offers.py index 20fee3a..6603bf1 100644 --- a/api/controllers/offers.py +++ b/app/controllers/offers.py @@ -2,8 +2,8 @@ Controller for offer selection endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import basic_auth_required -from api.models import SelectOffersRequest, SelectOffersResponse, Offer +from app.middleware import basic_auth_required +from app.models import SelectOffersRequest, SelectOffersResponse, Offer import logging # Configure logger diff --git a/api/controllers/penal.py b/app/controllers/penal.py similarity index 94% rename from api/controllers/penal.py rename to app/controllers/penal.py index a9321ba..e5c6636 100644 --- a/api/controllers/penal.py +++ b/app/controllers/penal.py @@ -2,8 +2,8 @@ Controller for penal charge endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import api_key_required -from api.models import PenalChargeRequest, PenalChargeResponse +from app.middleware import api_key_required +from app.models import PenalChargeRequest, PenalChargeResponse import logging # Configure logger diff --git a/api/controllers/rac.py b/app/controllers/rac.py similarity index 95% rename from api/controllers/rac.py rename to app/controllers/rac.py index 8cc45c6..5e5aa66 100644 --- a/api/controllers/rac.py +++ b/app/controllers/rac.py @@ -2,8 +2,8 @@ Controller for Risk Acceptance Criteria check endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import api_key_required -from api.models import RACCheckRequest, RACCheckResponse, RACResponse +from app.middleware import api_key_required +from app.models import RACCheckRequest, RACCheckResponse, RACResponse import logging # Configure logger diff --git a/api/controllers/repayment.py b/app/controllers/repayment.py similarity index 94% rename from api/controllers/repayment.py rename to app/controllers/repayment.py index 7dbffd8..a8039e9 100644 --- a/api/controllers/repayment.py +++ b/app/controllers/repayment.py @@ -2,8 +2,8 @@ Controller for repayment endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import basic_auth_required -from api.models import RepaymentRequest, RepaymentResponse +from app.middleware import basic_auth_required +from app.models import RepaymentRequest, RepaymentResponse import logging # Configure logger diff --git a/api/controllers/sms.py b/app/controllers/sms.py similarity index 97% rename from api/controllers/sms.py rename to app/controllers/sms.py index 580a5ff..5fcc36d 100644 --- a/api/controllers/sms.py +++ b/app/controllers/sms.py @@ -2,8 +2,8 @@ Controller for SMS notification endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import api_key_required -from api.models import SMSRequest, SMSResponse +from app.middleware import api_key_required +from app.models import SMSRequest, SMSResponse import logging # Configure logger diff --git a/api/controllers/token.py b/app/controllers/token.py similarity index 94% rename from api/controllers/token.py rename to app/controllers/token.py index 2787dd8..37fdde4 100644 --- a/api/controllers/token.py +++ b/app/controllers/token.py @@ -2,8 +2,8 @@ Controller for token validation endpoints. """ from flask import Blueprint, request, jsonify -from api.middleware import api_key_required -from api.models import ValidateTokenRequest, ValidateTokenResponse +from app.middleware import api_key_required +from app.models import ValidateTokenRequest, ValidateTokenResponse import logging # Configure logger diff --git a/api/controllers/transaction.py b/app/controllers/transaction.py similarity index 96% rename from api/controllers/transaction.py rename to app/controllers/transaction.py index 13bca95..18adcbf 100644 --- a/api/controllers/transaction.py +++ b/app/controllers/transaction.py @@ -3,8 +3,8 @@ Controller for transaction check endpoints. """ from flask import Blueprint, request, jsonify from flask.typing import ResponseReturnValue -from api.middleware import api_key_required -from api.models import ( +from app.middleware import api_key_required +from app.models import ( TransactionCheckRequest, TransactionCheckResponse, NewTransactionCheckRequest, NewTransactionCheckResponse, TransactionData @@ -54,7 +54,7 @@ def transaction_check() -> ResponseReturnValue: # Create request model req = TransactionCheckRequest.from_dict(data) - # Process transaction check (this would connect to the business logic) + # Process transaction check (this would connect to your business logic) # For demonstration, we'll return a mock response # Create response based on transaction type @@ -124,7 +124,7 @@ def new_transaction_check() -> ResponseReturnValue: # Create request model req = NewTransactionCheckRequest.from_dict(data) - # Process new transaction check (this would connect to the business logic) + # Process new transaction check (this would connect to your business logic) # For demonstration, we'll return a mock response # Create transaction data based on transaction type diff --git a/api/middleware.py b/app/middleware.py similarity index 100% rename from api/middleware.py rename to app/middleware.py diff --git a/api/models.py b/app/models.py similarity index 99% rename from api/models.py rename to app/models.py index a986655..54ca916 100644 --- a/api/models.py +++ b/app/models.py @@ -11,9 +11,6 @@ T = TypeVar('T', bound='BaseModel') class BaseModel: """Base model with serialization capabilities.""" - def __init__(self): - self.type_field = None - def to_dict(self) -> Dict[str, Any]: """Convert model to dictionary.""" result = {k: v for k, v in asdict(self).items() if v is not None} diff --git a/api/routes.py b/app/routes.py similarity index 59% rename from api/routes.py rename to app/routes.py index e41735a..04c42d0 100644 --- a/api/routes.py +++ b/app/routes.py @@ -15,21 +15,21 @@ def register_blueprints(app: Flask) -> None: app: Flask application instance """ # Import controllers - from api.controllers.eligibility import eligibility_bp - from api.controllers.offers import offers_bp - from api.controllers.loan import loan_bp - from api.controllers.repayment import repayment_bp - from api.controllers.consent import consent_bp - from api.controllers.notification import notification_bp - from api.controllers.rac import rac_bp - from api.controllers.disbursement import disbursement_bp - from api.controllers.collection import collection_bp - from api.controllers.transaction import transaction_bp - from api.controllers.penal import penal_bp - from api.controllers.token import token_bp - from api.controllers.lien import lien_bp - from api.controllers.sms import sms_bp - from api.controllers.health import health_bp + from app.controllers.eligibility import eligibility_bp + from app.controllers.offers import offers_bp + from app.controllers.loan import loan_bp + from app.controllers.repayment import repayment_bp + from app.controllers.consent import consent_bp + from app.controllers.notification import notification_bp + from app.controllers.rac import rac_bp + from app.controllers.disbursement import disbursement_bp + from app.controllers.collection import collection_bp + from app.controllers.transaction import transaction_bp + from app.controllers.penal import penal_bp + from app.controllers.token import token_bp + from app.controllers.lien import lien_bp + from app.controllers.sms import sms_bp + from app.controllers.health import health_bp # Create main API blueprint api_bp = Blueprint('api', __name__, url_prefix='/v1/api/salary') diff --git a/config.py b/config.py index 9869efe..d3e6efc 100644 --- a/config.py +++ b/config.py @@ -14,8 +14,8 @@ class Config: """Base configuration class.""" DEBUG: bool = os.environ.get('DEBUG', 'False').lower() == 'true' TESTING: bool = os.environ.get('TESTING', 'False').lower() == 'true' - PORT: int = int(os.environ.get('PORT', 8080)) # Changed default port to 8080 - + PORT: int = int(os.environ.get('PORT', 8080)) # Changed default from 5000 to 8080 + # API credentials API_USERNAME: str = os.environ.get('API_USERNAME', 'admin') API_PASSWORD: str = os.environ.get('API_PASSWORD', 'password') diff --git a/docker.compose.yaml b/docker-compose.yaml similarity index 81% rename from docker.compose.yaml rename to docker-compose.yaml index ed4bf2a..74d6411 100644 --- a/docker.compose.yaml +++ b/docker-compose.yaml @@ -1,13 +1,11 @@ -version: '3.8' - services: api: build: . ports: - - "5000:5000" + - "8080:8080" environment: - DEBUG=False - - PORT=5000 + - PORT=8080 - API_USERNAME=admin - API_PASSWORD=password - SIMBRELLA_APP_ID=your_app_id @@ -17,7 +15,7 @@ services: - ./logs:/app/logs restart: unless-stopped healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:5000/v1/api/salary/health"] + test: ["CMD", "curl", "-f", "http://localhost:8080/v1/api/salary/health"] interval: 30s timeout: 10s retries: 3 @@ -34,5 +32,6 @@ services: # - postgres_data:/var/lib/postgresql/data # restart: unless-stopped +# Uncomment if you add the database service # volumes: # postgres_data: \ No newline at end of file diff --git a/simbrella_api.csv b/simbrella_api.csv new file mode 100644 index 0000000..e02abfc --- /dev/null +++ b/simbrella_api.csv @@ -0,0 +1 @@ + diff --git a/app.py b/wsgi.py similarity index 94% rename from app.py rename to wsgi.py index 5722e2c..e350064 100644 --- a/app.py +++ b/wsgi.py @@ -5,8 +5,8 @@ This module serves as the entry point for the Flask application. from flask import Flask from flask.typing import ResponseReturnValue from config import Config -from api.routes import register_blueprints -from api.middleware import register_middleware +from app.routes import register_blueprints +from app.middleware import register_middleware import logging def create_app(config_class=Config) -> Flask: