Skip to content

Commit

Permalink
Rearrange validation flow
Browse files Browse the repository at this point in the history
  • Loading branch information
NghiaDTr committed Jul 18, 2024
1 parent 751d7cf commit 2efd722
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 26 deletions.
23 changes: 16 additions & 7 deletions processor/src/controllers/payment.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PaymentReference, Payment } from '@commercetools/platform-sdk';
import { ConnectorActions } from '../utils/constant.utils';
import { validateCommerceToolsPaymentPayload } from '../validators/payment.validators';
import CustomError from '../errors/custom.error';
import SkipError from '../errors/skip.error';

/**
* Handle the cart controller according to the action
Expand All @@ -19,20 +20,28 @@ export const paymentController = async (
): Promise<ControllerResponseType> => {
const ctPayment: Payment = JSON.parse(JSON.stringify(resource)).obj;

validateCommerceToolsPaymentPayload(action, ctPayment);

const controllerAction = determinePaymentAction(ctPayment);

if (controllerAction.errorMessage !== '') {
throw new CustomError(400, controllerAction.errorMessage as string);
}

if (controllerAction.action === ConnectorActions.NoAction) {
throw new SkipError('SCTM - No payment actions matched');
}

validateCommerceToolsPaymentPayload(action, controllerAction.action, ctPayment);

switch (controllerAction.action) {
case ConnectorActions.GetPaymentMethods:
return await handleListPaymentMethodsByPayment(ctPayment);
case ConnectorActions.CreatePayment:
return await handleCreatePayment(ctPayment);
case ConnectorActions.NoAction:
return {
statusCode: 200,
};
default:
throw new CustomError(400, controllerAction.errorMessage ?? '');
if (controllerAction.errorMessage === '') {
throw new SkipError('SCTM - No payment actions matched');
}

throw new CustomError(400, controllerAction.errorMessage as string);
}
};
5 changes: 3 additions & 2 deletions processor/src/controllers/processor.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { paymentController } from './payment.controller';
import CustomError from '../errors/custom.error';
import SkipError from '../errors/skip.error';
import { apiError } from '../api/error.api';
import { formatErrorResponse } from '../errors/mollie.error';

/**
* Exposed service endpoint.
Expand Down Expand Up @@ -44,8 +45,8 @@ export const post = async (request: Request, response: Response, next: NextFunct
}
if (error instanceof CustomError) {
return apiError(response, error.errors);
} else {
next(error);
}

return apiError(response, formatErrorResponse(error).errors);
}
};
3 changes: 1 addition & 2 deletions processor/src/service/payment.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,14 @@ export const handleListPaymentMethodsByPayment = async (ctPayment: Payment): Pro
try {
const mollieOptions = await mapCommercetoolsPaymentCustomFieldsToMollieListParams(ctPayment);
const methods: List<Method> = await listPaymentMethods(mollieOptions);

const availableMethods = JSON.stringify({
count: methods.length,
methods: methods.length ? methods : [],
});

const ctUpdateActions: UpdateAction[] = [setCustomFields(CustomFields.payment.response, availableMethods)];

const hasCardPayment = methods.findIndex((method: Method) => method.id === PaymentMethod.creditcard);
const hasCardPayment = methods.find((method: Method) => method.id === PaymentMethod.creditcard);

if (hasCardPayment) {
ctUpdateActions.push(setCustomFields(CustomFields.payment.profileId, readConfiguration().mollie.profileId));
Expand Down
6 changes: 3 additions & 3 deletions processor/src/utils/paymentAction.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const determinePaymentAction = (ctPayment?: Payment): DeterminePaymentAct
};
}

const { key, transactions } = ctPayment;
const { id, key, transactions } = ctPayment;

const initialChargeTransactions: CTTransaction[] = [];
const pendingChargeTransactions: CTTransaction[] = [];
Expand Down Expand Up @@ -70,15 +70,15 @@ export const determinePaymentAction = (ctPayment?: Payment): DeterminePaymentAct
break;

// Create Payment
case !!key &&
case (!!key || !!id) &&
initialChargeTransactions.length === 1 &&
!successChargeTransactions.length &&
!pendingChargeTransactions.length:
action = ConnectorActions.CreatePayment;
break;
default:
action = ConnectorActions.NoAction;
errorMessage = 'SCTM - No payment actions matched';
logger.warn('SCTM - No payment actions matched');
}

return {
Expand Down
21 changes: 14 additions & 7 deletions processor/src/validators/payment.validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { PaymentMethod as MolliePaymentMethods } from '@mollie/api-client';
import SkipError from '../errors/skip.error';
import CustomError from '../errors/custom.error';
import { logger } from '../utils/logger.utils';
import { CustomFields } from '../utils/constant.utils';
import { ConnectorActions, CustomFields } from '../utils/constant.utils';

/**
* Checks if the given action is either 'Create' or 'Update'.
Expand Down Expand Up @@ -56,7 +56,7 @@ export const hasValidPaymentMethod: (method: string | undefined) => boolean = (m
* The `errorMessage` property contains the error message if the input is invalid.
* @param ctPayment
*/
export const checkPaymentMethodInput = (ctPayment: CTPayment): true | CustomError => {
export const checkPaymentMethodInput = (connectorAction: string, ctPayment: CTPayment): true | CustomError => {
const CTPaymentMethod = ctPayment.paymentMethodInfo?.method ?? '';
const [method] = CTPaymentMethod.split(',');

Expand Down Expand Up @@ -128,16 +128,23 @@ export const checkAmountPlanned = (ctPayment: CTPayment): true | CustomError =>
/**
* Validates the payload of a CommerceTools payment based on the provided action and payment object.
*
* @param {string} action - The action to perform on the payment.
* @param {CTPayment} CTPayment - The CommerceTools payment object to validate.
* @param {string} extensionAction - The action to perform on the payment.
* @param {string} controllerAction - The determined action that need to be done with the payment.
* @param {CTPayment} ctPayment - The CommerceTools payment object to validate.
* @return {void} - An object containing the validated action and an error message if validation fails.
*/
export const validateCommerceToolsPaymentPayload = (action: string, ctPayment: CTPayment): void => {
checkExtensionAction(action);
export const validateCommerceToolsPaymentPayload = (
extensionAction: string,
connectorAction: string,
ctPayment: CTPayment,
): void => {
checkExtensionAction(extensionAction);

checkPaymentInterface(ctPayment);

// checkPaymentMethodInput(ctPayment);
if (connectorAction === ConnectorActions.CreatePayment) {
checkPaymentMethodInput(connectorAction, ctPayment);
}

checkAmountPlanned(ctPayment);
};
8 changes: 7 additions & 1 deletion processor/tests/controllers/payment.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ describe('Test payment.controller.ts', () => {
} as unknown as Payment,
} as PaymentReference;

(determinePaymentAction as jest.Mock).mockReturnValue({
action: ConnectorActions.GetPaymentMethods,
errorMessage: '',
});

(validateCommerceToolsPaymentPayload as jest.Mock).mockImplementationOnce(() => {
throw new CustomError(400, 'dummy message');
});
Expand All @@ -61,7 +66,8 @@ describe('Test payment.controller.ts', () => {
expect(error).toBeInstanceOf(CustomError);
expect(error.statusCode).toBe(400);
expect(error.message).toBe('dummy message');
expect(determinePaymentAction).toBeCalledTimes(0);
expect(determinePaymentAction).toBeCalledTimes(1);
expect(validateCommerceToolsPaymentPayload).toBeCalledTimes(1);
expect(handleListPaymentMethodsByPayment).toBeCalledTimes(0);
expect(handleCreatePayment).toBeCalledTimes(0);
});
Expand Down
51 changes: 47 additions & 4 deletions processor/tests/validators/payment.validators.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { ConnectorActions } from './../../src/utils/constant.utils';
import { Payment } from '@commercetools/platform-sdk';
import {
checkExtensionAction,
checkPaymentInterface,
checkPaymentMethodInput,
checkPaymentMethodSpecificParameters,
hasValidPaymentMethod,
validateCommerceToolsPaymentPayload,
} from './../../src/validators/payment.validators';
import { describe, it, expect, jest, afterEach } from '@jest/globals';
import CustomError from '../../src/errors/custom.error';
Expand Down Expand Up @@ -149,7 +151,7 @@ describe('checkPaymentMethodInput', () => {
jest.clearAllMocks(); // Clear all mocks after each test case
});

it('should throw CustomError and a correct error message if the payment method is not defined', () => {
it('should throw CustomError and a correct error message if the payment method is not defined when trying to create a Mollie payment', () => {
const CTPayment: Payment = {
id: '5c8b0375-305a-4f19-ae8e-07806b101999',
version: 1,
Expand All @@ -168,7 +170,7 @@ describe('checkPaymentMethodInput', () => {
};

try {
checkPaymentMethodInput(CTPayment);
checkPaymentMethodInput(ConnectorActions.CreatePayment, CTPayment);
} catch (error: any) {
expect(error).toBeInstanceOf(CustomError);
expect(error.message).toBe(
Expand Down Expand Up @@ -198,7 +200,7 @@ describe('checkPaymentMethodInput', () => {
};

try {
checkPaymentMethodInput(CTPayment);
checkPaymentMethodInput(ConnectorActions.CreatePayment, CTPayment);
} catch (error: any) {
expect(error).toBeInstanceOf(CustomError);
expect(error.message).toBe(
Expand Down Expand Up @@ -227,7 +229,7 @@ describe('checkPaymentMethodInput', () => {
},
};

expect(checkPaymentMethodInput(CTPayment)).toBe(true);
expect(checkPaymentMethodInput(ConnectorActions.CreatePayment, CTPayment)).toBe(true);
});
});

Expand Down Expand Up @@ -361,3 +363,44 @@ describe('checkPaymentMethodSpecificParameters', () => {
expect(checkPaymentMethodSpecificParameters(CTPayment, CTPayment.paymentMethodInfo.method as string)).toBe(true);
});
});

import * as paymentValidators from '../../src/validators/payment.validators';

describe('validateCommerceToolsPaymentPayload', () => {
jest.spyOn(paymentValidators, 'checkPaymentMethodInput');

it('should not call the checkPaymentMethodInput when the action is not "CreatePayment"', () => {
try {
validateCommerceToolsPaymentPayload('Update', ConnectorActions.GetPaymentMethods, {} as Payment);
} catch (error: unknown) {
expect(checkPaymentMethodInput).toBeCalledTimes(0);
}
});

it('should call the checkPaymentMethodInput when the action is "CreatePayment"', () => {
try {
const CTPayment: Payment = {
id: '5c8b0375-305a-4f19-ae8e-07806b101999',
version: 1,
createdAt: '2024-07-04T14:07:35.625Z',
lastModifiedAt: '2024-07-04T14:07:35.625Z',
amountPlanned: {
type: 'centPrecision',
currencyCode: 'EUR',
centAmount: 1000,
fractionDigits: 2,
},
paymentStatus: {},
transactions: [],
interfaceInteractions: [],
paymentMethodInfo: {
paymentInterface: 'Mollie',
},
};

validateCommerceToolsPaymentPayload('Update', ConnectorActions.CreatePayment, CTPayment);
} catch (error: unknown) {
expect(checkPaymentMethodInput).toBeCalledTimes(1);
}
});
});

0 comments on commit 2efd722

Please sign in to comment.