From d9c99627ae9b749ec145a23030269cde959129b7 Mon Sep 17 00:00:00 2001 From: VivianDee <115420678+VivianDee@users.noreply.github.com> Date: Mon, 31 Mar 2025 14:50:22 +0100 Subject: [PATCH] [update]: transaction logging and account ownership --- app/api/enums/__init__.py | 2 +- app/api/enums/transaction_type.py | 8 +++- app/api/services/base_service.py | 10 ++--- app/api/services/customer_consent.py | 48 ++++++++++++++++------- app/api/services/eligibility_check.py | 24 +++++++----- app/api/services/loan_status.py | 47 ++++++++++++++++------ app/api/services/notification_callback.py | 17 +++++++- app/api/services/provide_loan.py | 45 +++++++++++++++------ app/api/services/repayment.py | 38 ++++++++++++++---- app/api/services/select_offer.py | 43 ++++++++++++++------ 10 files changed, 203 insertions(+), 79 deletions(-) diff --git a/app/api/enums/__init__.py b/app/api/enums/__init__.py index cf543fd..2e3a1d8 100644 --- a/app/api/enums/__init__.py +++ b/app/api/enums/__init__.py @@ -1 +1 @@ -from .transaction_type import transaction_type \ No newline at end of file +from .transaction_type import TransactionType \ No newline at end of file diff --git a/app/api/enums/transaction_type.py b/app/api/enums/transaction_type.py index 2f5ac09..7d14546 100644 --- a/app/api/enums/transaction_type.py +++ b/app/api/enums/transaction_type.py @@ -1,6 +1,10 @@ from enum import Enum -class transaction_type(str, Enum): +class TransactionType(str, Enum): ELIGIBILITY_CHECK = "eligibility_check" - PAYMENT = "payment" + CUSTOMER_CONSENT = "customer_consent" + LOAN_STATUS = "loan_status" + NOTIFICATION_CALLBACK = "notification_callback" + PROVIDE_LOAN = "provide_loan" REPAYMENT = "repayment" + SELECT_OFFER = "select_offer" diff --git a/app/api/services/base_service.py b/app/api/services/base_service.py index 453f629..40413c3 100644 --- a/app/api/services/base_service.py +++ b/app/api/services/base_service.py @@ -1,5 +1,5 @@ from app.models import Customer, Account, Transaction -from app.api.enums import transaction_type +from app.api.enums import TransactionType from flask import jsonify from marshmallow import ValidationError import logging @@ -40,18 +40,16 @@ class BaseService: Check if the provided account belongs to the customer. """ is_valid = Account.is_valid_account(account_id, customer_id) - - if not is_valid: - raise ValueError("Account does not belong to customer") + return is_valid @classmethod - def create_transaction(cls, validated_data): + def log_transaction(cls, validated_data): """ Create a new transaction. """ return Transaction.create_transaction( id=validated_data.get("transactionId"), account_id=validated_data.get("accountId"), - type=BaseService.TRANSACTION_TYPE, + type=cls.TRANSACTION_TYPE, channel=validated_data.get("channel"), ) diff --git a/app/api/services/customer_consent.py b/app/api/services/customer_consent.py index 5db10e4..2d1048a 100644 --- a/app/api/services/customer_consent.py +++ b/app/api/services/customer_consent.py @@ -2,10 +2,14 @@ from flask import request, jsonify from app.api.services.base_service import BaseService from marshmallow import ValidationError from app.utils.logger import logger -from app.api.schemas.customer_consent import CustomerConsentSchema +from app.api.schemas.customer_consent import CustomerConsentSchema +from app.api.services.base_service import BaseService +from app.api.enums import TransactionType class CustomerConsentService(BaseService): + TRANSACTION_TYPE = TransactionType.CUSTOMER_CONSENT + @staticmethod def process_request(data): """ @@ -18,34 +22,50 @@ class CustomerConsentService(BaseService): dict: A standardized response. """ try: - logger.info("Processing CustomerConsent request") - # Validate input data using the CustomerConsent schema - schema = CustomerConsentSchema() - validated_data = schema.load(data) + validated_data = CustomerConsentService.validate_data(data, CustomerConsentSchema()) + account_id = validated_data.get('accountId') + customer_id = validated_data.get('customerId') + if(CustomerConsentService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + transaction = CustomerConsentService.log_transaction(validated_data = validated_data) + + if not transaction: + logger.error(f"Failed to log transaction") + return jsonify({ + "message": "Failed to log transaction." + }), 400 + else: + return jsonify({ + "message": "Invalid Customer or Account" + }), 400 + + # Simulated processing logic response_data = { "resultCode": "00", "resultDescription": "Request is received" } - - # return ResponseHelper.success( - # data=response_data, - # message="Customer consent processed successfully" - # ) - return response_data - + except ValidationError as err: - logger.error(f"Validation Error: {err.messages}") + + logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + return jsonify({ "message": "Validation exception" }) , 422 + + except ValueError as err: + logger.error(f"{getattr(err, 'messages', str(err))}") + + return jsonify({ + "message": str(err) + }) , 400 except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) return jsonify({ "message": "Internal Server Error" - }) , 500 + }) , 500 \ No newline at end of file diff --git a/app/api/services/eligibility_check.py b/app/api/services/eligibility_check.py index 146218f..37fcaa1 100644 --- a/app/api/services/eligibility_check.py +++ b/app/api/services/eligibility_check.py @@ -3,10 +3,10 @@ from app.utils.logger import logger from app.api.services.base_service import BaseService from app.api.schemas.eligibility_check import EligibilityCheckSchema from marshmallow import ValidationError -from app.api.enums import transaction_type +from app.api.enums import TransactionType class EligibilityCheckService(BaseService): - TRANSACTION_TYPE = transaction_type.ELIGIBILITY_CHECK + TRANSACTION_TYPE = TransactionType.ELIGIBILITY_CHECK @staticmethod def process_request(data): @@ -27,20 +27,17 @@ class EligibilityCheckService(BaseService): customer = EligibilityCheckService.get_or_create_customer(validated_data = validated_data) - logger.error(account_id) - logger.error(customer_id) - if (EligibilityCheckService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): - transaction = EligibilityCheckService.create_transaction(validated_data = validated_data) + transaction = EligibilityCheckService.log_transaction(validated_data = validated_data) if not transaction: - logger.error(f"Transaction creation failed") + logger.error(f"Failed to log transaction") return jsonify({ - "message": "Transaction creation failed." + "message": "Failed to log transaction." }), 400 else: return jsonify({ - "message": "Invalid account" + "message": "Invalid Customer or Account" }), 400 @@ -74,13 +71,20 @@ class EligibilityCheckService(BaseService): } return response_data - except (ValidationError, ValueError) as err: + except ValidationError as err: logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") return jsonify({ "message": "Validation exception" }) , 422 + + except ValueError as err: + logger.error(f"{getattr(err, 'messages', str(err))}") + + return jsonify({ + "message": str(err) + }) , 400 except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) diff --git a/app/api/services/loan_status.py b/app/api/services/loan_status.py index b163e89..95d5048 100644 --- a/app/api/services/loan_status.py +++ b/app/api/services/loan_status.py @@ -1,9 +1,14 @@ from flask import request, jsonify from marshmallow import ValidationError from app.utils.logger import logger -from app.api.schemas.loan_status import LoanStatusSchema +from app.api.schemas.loan_status import LoanStatusSchema +from app.api.services.base_service import BaseService +from app.api.enums import TransactionType + + +class LoanStatusService(BaseService): + TRANSACTION_TYPE = TransactionType.LOAN_STATUS -class LoanStatusService: @staticmethod def process_request(data): """ @@ -16,11 +21,23 @@ class LoanStatusService: dict: A standardized response. """ try: - logger.info("Processing LoanStatus request") + validated_data = LoanStatusService.validate_data(data, LoanStatusSchema()) + account_id = validated_data.get('accountId') + customer_id = validated_data.get('customerId') - # Validate input data using the imported schema - schema = LoanStatusSchema() - validated_data = schema.load(data) # Raises ValidationError if invalid + if (LoanStatusService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + transaction = LoanStatusService.log_transaction(validated_data = validated_data) + + if not transaction: + logger.error(f"Failed to log transaction") + return jsonify({ + "message": "Failed to log transaction." + }), 400 + else: + return jsonify({ + "message": "Invalid Customer or Account" + }), 400 + loans = [ { @@ -46,21 +63,25 @@ class LoanStatusService: } - # return ResponseHelper.success( - # data=response_data, - # message="Loan information retrieved successfully" - # ) - return response_data except ValidationError as err: - logger.error(f"Validation Error: {err.messages}") + + logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + return jsonify({ "message": "Validation exception" }) , 422 + + except ValueError as err: + logger.error(f"{getattr(err, 'messages', str(err))}") + + return jsonify({ + "message": str(err) + }) , 400 except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) return jsonify({ "message": "Internal Server Error" - }) , 500 + }) , 500 \ No newline at end of file diff --git a/app/api/services/notification_callback.py b/app/api/services/notification_callback.py index 2c876db..b3a1c8b 100644 --- a/app/api/services/notification_callback.py +++ b/app/api/services/notification_callback.py @@ -1,9 +1,13 @@ from flask import request, jsonify from marshmallow import ValidationError +from app.api.services.base_service import BaseService +from app.api.enums import TransactionType from app.utils.logger import logger from app.api.schemas.notification_callback import NotificationCallbackSchema -class NotificationCallbackService: +class NotificationCallbackService(BaseService): + TRANSACTION_TYPE = TransactionType.NOTIFICATION_CALLBACK + @staticmethod def process_request(data): """ @@ -37,10 +41,19 @@ class NotificationCallbackService: return response_data except ValidationError as err: - logger.error(f"Validation Error: {err.messages}") + + logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + return jsonify({ "message": "Validation exception" }) , 422 + + except ValueError as err: + logger.error(f"{getattr(err, 'messages', str(err))}") + + return jsonify({ + "message": str(err) + }) , 400 except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) diff --git a/app/api/services/provide_loan.py b/app/api/services/provide_loan.py index 8d57343..49dcefe 100644 --- a/app/api/services/provide_loan.py +++ b/app/api/services/provide_loan.py @@ -1,9 +1,14 @@ from flask import request, jsonify from marshmallow import ValidationError +from app.api.services.base_service import BaseService +from app.api.enums import TransactionType from app.utils.logger import logger from app.api.schemas.provide_loan import ProvideLoanSchema -class ProvideLoanService: +class ProvideLoanService(BaseService): + TRANSACTION_TYPE = TransactionType.PROVIDE_LOAN + + @staticmethod def process_request(data): """ @@ -16,13 +21,24 @@ class ProvideLoanService: dict: A standardized response. """ try: - logger.info("Processing ProvideLoan request") + validated_data = ProvideLoanService.validate_data(data, ProvideLoanSchema()) + account_id = validated_data.get('accountId') + customer_id = validated_data.get('customerId') - # Validate input data using the imported schema - schema = ProvideLoanSchema() - validated_data = schema.load(data) # Raises ValidationError if invalid + if (ProvideLoanService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + transaction = ProvideLoanService.log_transaction(validated_data = validated_data) - # Business logic - providing a loan + if not transaction: + logger.error(f"Failed to log transaction") + return jsonify({ + "message": "Failed to log transaction." + }), 400 + else: + return jsonify({ + "message": "Invalid Customer or Account" + }), 400 + + response_data = { "requestId": "202111170001371256908", "transactionId": "Tr201712RK9232P115", @@ -34,21 +50,26 @@ class ProvideLoanService: } - # return ResponseHelper.success( - # data=response_data, - # message="Loan successfully provided" - # ) return response_data except ValidationError as err: - logger.error(f"Validation Error: {err.messages}") + + logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + return jsonify({ "message": "Validation exception" }) , 422 + + except ValueError as err: + logger.error(f"{getattr(err, 'messages', str(err))}") + + return jsonify({ + "message": str(err) + }) , 400 except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) return jsonify({ "message": "Internal Server Error" - }) , 500 + }) , 500 \ No newline at end of file diff --git a/app/api/services/repayment.py b/app/api/services/repayment.py index 1aff386..7a53452 100644 --- a/app/api/services/repayment.py +++ b/app/api/services/repayment.py @@ -1,9 +1,13 @@ from flask import request, jsonify from marshmallow import ValidationError from app.utils.logger import logger -from app.api.schemas.repayment import RepaymentSchema +from app.api.schemas.repayment import RepaymentSchema +from app.api.services.base_service import BaseService +from app.api.enums import TransactionType + +class RepaymentService(BaseService): + TRANSACTION_TYPE = TransactionType.REPAYMENT -class RepaymentService: @staticmethod def process_request(data): """ @@ -16,11 +20,22 @@ class RepaymentService: dict: A standardized response. """ try: - logger.info("Processing Repayment request") + validated_data = RepaymentService.validate_data(data, RepaymentSchema()) + account_id = validated_data.get('accountId') + customer_id = validated_data.get('customerId') - # Validate input data using the Repayment schema - schema = RepaymentSchema() - validated_data = schema.load(data) # Raises ValidationError if invalid + if (RepaymentService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + transaction = RepaymentService.log_transaction(validated_data = validated_data) + + if not transaction: + logger.error(f"Failed to log transaction") + return jsonify({ + "message": "Failed to log transaction." + }), 400 + else: + return jsonify({ + "message": "Invalid Customer or Account" + }), 400 # Simulated processing logic response_data = { @@ -39,10 +54,19 @@ class RepaymentService: return response_data except ValidationError as err: - logger.error(f"Validation Error: {err.messages}") + + logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + return jsonify({ "message": "Validation exception" }) , 422 + + except ValueError as err: + logger.error(f"{getattr(err, 'messages', str(err))}") + + return jsonify({ + "message": str(err) + }) , 400 except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) diff --git a/app/api/services/select_offer.py b/app/api/services/select_offer.py index 8656cbc..9d502c5 100644 --- a/app/api/services/select_offer.py +++ b/app/api/services/select_offer.py @@ -1,9 +1,13 @@ from flask import request, jsonify from marshmallow import ValidationError +from app.api.services.base_service import BaseService +from app.api.enums import TransactionType from app.utils.logger import logger from app.api.schemas.select_offer import SelectOfferSchema -class SelectOfferService: +class SelectOfferService(BaseService): + TRANSACTION_TYPE = TransactionType.SELECT_OFFER + @staticmethod def process_request(data): """ @@ -16,12 +20,23 @@ class SelectOfferService: dict: A standardized response. """ try: - logger.info("Processing SelectOffer request") + validated_data = SelectOfferService.validate_data(data, SelectOfferSchema()) + account_id = validated_data.get('accountId') + customer_id = validated_data.get('customerId') - # Validate input data using the imported schema - schema = SelectOfferSchema() - validated_data = schema.load(data) # Raises ValidationError if invalid + if (SelectOfferService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + transaction = SelectOfferService.log_transaction(validated_data = validated_data) + if not transaction: + logger.error(f"Failed to log transaction") + return jsonify({ + "message": "Failed to log transaction." + }), 400 + else: + return jsonify({ + "message": "Invalid Customer or Account" + }), 400 + offers = [ { "offerId": "14451", @@ -54,21 +69,25 @@ class SelectOfferService: } - # return ResponseHelper.success( - # data=response_data, - # message="Offer selection completed successfully" - # ) - return response_data except ValidationError as err: - logger.error(f"Validation Error: {err.messages}") + + logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + return jsonify({ "message": "Validation exception" }) , 422 + + except ValueError as err: + logger.error(f"{getattr(err, 'messages', str(err))}") + + return jsonify({ + "message": str(err) + }) , 400 except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) return jsonify({ "message": "Internal Server Error" - }) , 500 + }) , 500 \ No newline at end of file