Skip to content

Commit

Permalink
chore: Updating Payment State Polling to use a Custom Routine
Browse files Browse the repository at this point in the history
  • Loading branch information
grmartin committed Jul 5, 2023
1 parent d37cd30 commit e9d2272
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 15 deletions.
13 changes: 5 additions & 8 deletions src/payment/data/actions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createRoutine } from 'redux-saga-routines';
import { createCustomRoutine } from './utils';

// Routines are action + action creator pairs in a series.
// Actions adhere to the flux standard action format.
Expand All @@ -10,8 +11,11 @@ import { createRoutine } from 'redux-saga-routines';
// fetchBasket.SUCCESS | fetchBasket.success()
// fetchBasket.FAILURE | fetchBasket.failure()
// fetchBasket.FULFILL | fetchBasket.fulfill()
// fetchBasket.REQUEST | fetchBasket.request()
// fetchBasket.<CUSTOM> | fetchBasket.<custom>()
//
// Created with redux-saga-routines

export const fetchCaptureKey = createRoutine('FETCH_CAPTURE_KEY');
export const fetchClientSecret = createRoutine('FETCH_CLIENT_SECRET');
export const submitPayment = createRoutine('SUBMIT_PAYMENT');
Expand All @@ -20,7 +24,7 @@ export const fetchActiveOrder = createRoutine('FETCH_ACTIVE_ORDER');
export const addCoupon = createRoutine('ADD_COUPON');
export const removeCoupon = createRoutine('REMOVE_COUPON');
export const updateQuantity = createRoutine('UPDATE_QUANTITY');
export const pollPaymentState = createRoutine('UPDATE_PAYMENT_STATE');
export const pollPaymentState = createCustomRoutine('UPDATE_PAYMENT_STATE', ['RECEIVED']);

// Actions and their action creators
export const BASKET_DATA_RECEIVED = 'BASKET_DATA_RECEIVED';
Expand Down Expand Up @@ -76,10 +80,3 @@ export const clientSecretDataReceived = clientSecret => ({
type: CLIENT_SECRET_DATA_RECEIVED,
payload: clientSecret,
});

export const PAYMENT_STATE_DATA_RECEIVED = 'PAYMENT_STATE_DATA_RECEIVED';

export const paymentStateDataReceived = paymentState => ({
type: PAYMENT_STATE_DATA_RECEIVED,
payload: paymentState,
});
3 changes: 1 addition & 2 deletions src/payment/data/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
CLIENT_SECRET_DATA_RECEIVED,
CLIENT_SECRET_PROCESSING,
MICROFORM_STATUS,
PAYMENT_STATE_DATA_RECEIVED,
fetchBasket,
submitPayment,
fetchCaptureKey,
Expand Down Expand Up @@ -197,7 +196,7 @@ const paymentState = (state = basketInitialState, action = null) => {
},
};

case PAYMENT_STATE_DATA_RECEIVED:
case pollPaymentState.RECEIVED:
return {
...state,
paymentState: action.payload.state,
Expand Down
3 changes: 1 addition & 2 deletions src/payment/data/redux.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
fetchBasket,
fetchActiveOrder,
pollPaymentState,
PAYMENT_STATE_DATA_RECEIVED,
} from './actions';
import { currencyDisclaimerSelector, paymentSelector } from './selectors';
import { localizedCurrencySelector } from './utils';
Expand Down Expand Up @@ -258,7 +257,7 @@ describe('redux tests', () => {
expect(triggerStore.getState().payment.basket.paymentStatePolling.keepPolling).toBe(true);
expect(triggerStore.getState().payment.basket.paymentState).toBe(PAYMENT_STATE.PENDING);

triggerStore.dispatch({ type: PAYMENT_STATE_DATA_RECEIVED, payload: { state: PAYMENT_STATE.COMPLETED } });
triggerStore.dispatch(pollPaymentState.received({ state: PAYMENT_STATE.COMPLETED }));
expect(triggerStore.getState().payment.basket.paymentStatePolling.keepPolling).toBe(false);
expect(triggerStore.getState().payment.basket.paymentState).toBe(PAYMENT_STATE.COMPLETED);

Expand Down
6 changes: 3 additions & 3 deletions src/payment/data/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
fetchCaptureKey,
clientSecretProcessing,
fetchClientSecret,
paymentStateDataReceived,
pollPaymentState,
} from './actions';

Expand Down Expand Up @@ -335,7 +334,8 @@ export function* handlePaymentState() {
}

const result = yield call(PaymentApiService.getCurrentPaymentState, paymentNumber, basketId);
yield put(paymentStateDataReceived(result));

yield put(pollPaymentState.received(result));

if (!(yield select(state => state.payment.basket.paymentStatePolling.keepPolling))) {
yield put(pollPaymentState.fulfill());
Expand All @@ -354,7 +354,7 @@ export function* handlePaymentState() {
throw innerError;
}

yield put(paymentStateDataReceived({ state: PAYMENT_STATE.HTTP_ERROR }));
yield put(pollPaymentState.received({ state: PAYMENT_STATE.HTTP_ERROR }));

if (yield select(state => state.payment.basket.paymentStatePolling.errorCount) < 1) {
// noinspection ExceptionCaughtLocallyJS
Expand Down
15 changes: 15 additions & 0 deletions src/payment/data/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import camelCase from 'lodash.camelcase';
import snakeCase from 'lodash.snakecase';
import { getConfig } from '@edx/frontend-platform';
import Cookies from 'universal-cookie';
import { createRoutineCreator, defaultRoutineStages } from 'redux-saga-routines';
import { ORDER_TYPES } from './constants';

export function modifyObjectKeys(object, modify) {
Expand Down Expand Up @@ -273,3 +274,17 @@ export const chainReducers = (reducers) => {
);
};
};

/**
* Create a Routine with Custom Steps
* @param {string} name Name of the Routine
* @param {string[]} addtlStages An Array of Additional Steps (these will be uppercased)
* @param {boolean=} keepDefaults If we should include the normal Routine Steps
* @returns {*} A Redux Saga Routine
*/
export function createCustomRoutine(name, addtlStages, keepDefaults = true) {
return createRoutineCreator([
...(keepDefaults ? defaultRoutineStages : []),
...addtlStages.map(x => x.toUpperCase()),
])(name);
}
49 changes: 49 additions & 0 deletions src/payment/data/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Factory } from 'rosie';
import '../__factories__/basket.factory';

import { defaultRoutineStages } from 'redux-saga-routines';
import { ORDER_TYPES } from './constants';
import {
AsyncActionType,
Expand All @@ -17,6 +18,7 @@ import {
SECS_AS_MS,
MINS_AS_MS,
chainReducers,
createCustomRoutine,
} from './utils';

describe('modifyObjectKeys', () => {
Expand Down Expand Up @@ -370,3 +372,50 @@ describe('chainReducers([reducers])', () => {
expect(firstStateResult.second_reducer_value).toEqual(undefined);
});
});

describe('createCustomRoutine', () => {
const tests = [
{
name: 'Additional Stage (UC) + Inherts Default Stages',
params: { name: 'TEST_ROUTINE_1', addtlStages: ['MEOW'], inheritDefaults: true },
},
{
name: 'Additional Stage (LC) + Inherts Default Stages',
params: { name: 'TEST_ROUTINE_2', addtlStages: ['meow'], inheritDefaults: true },
},
{
name: 'Additional Stage (LC) + Doesnt Inherit Default Stages',
params: { name: 'TEST_ROUTINE_3', addtlStages: ['woof'], inheritDefaults: false },
},
];

for (let i = 0, testPlan = tests[i]; i < tests.length; i++, testPlan = tests[i]) {
it(testPlan.name, () => {
/* eslint-disable no-underscore-dangle */ // We don't control the fact that we have to access _ props here.
const routineUnderTest = createCustomRoutine(
testPlan.params.name,
testPlan.params.addtlStages,
testPlan.params.inheritDefaults,
);

expect(routineUnderTest._PREFIX).toEqual(testPlan.params.name);

for (let si = 0, stageName = defaultRoutineStages[si];
si < defaultRoutineStages.length;
si++, stageName = defaultRoutineStages[si]) {
if (testPlan.params.inheritDefaults) {
expect(routineUnderTest._STAGES).toContain(stageName);
} else {
expect(routineUnderTest._STAGES).not.toContain(stageName);
}
}

for (let si = 0, stageName = testPlan.params.addtlStages[si];
si < testPlan.params.addtlStages.length;
si++, stageName = testPlan.params.addtlStages[si]) {
expect(routineUnderTest._STAGES).toContain(stageName.toUpperCase());
}
/* eslint-enable no-underscore-dangle */
});
}
});

0 comments on commit e9d2272

Please sign in to comment.