diff --git a/package-lock.json b/package-lock.json
index 524666e9f..f4b328c75 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -43,7 +43,6 @@
"react-use": "^17.5.1",
"redux": "^5.0.1",
"redux-logger": "^3.0.6",
- "redux-promise-middleware": "^6.2.0",
"redux-thunk": "^3.1.0",
"victory": "37.0.2",
"victory-create-container": "37.0.2"
@@ -20402,14 +20401,6 @@
"lodash.isplainobject": "^4.0.6"
}
},
- "node_modules/redux-promise-middleware": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/redux-promise-middleware/-/redux-promise-middleware-6.2.0.tgz",
- "integrity": "sha512-TEzfMeLX63gju2WqkdFQlQMvUGYzFvJNePIJJsBlbPHs3Txsbc/5Rjhmtha1XdMU6lkeiIlp1Qx7AR3Zo9he9g==",
- "peerDependencies": {
- "redux": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0"
- }
- },
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
diff --git a/package.json b/package.json
index de464fddb..721832f1b 100644
--- a/package.json
+++ b/package.json
@@ -110,7 +110,6 @@
"react-use": "^17.5.1",
"redux": "^5.0.1",
"redux-logger": "^3.0.6",
- "redux-promise-middleware": "^6.2.0",
"redux-thunk": "^3.1.0",
"victory": "37.0.2",
"victory-create-container": "37.0.2"
diff --git a/src/redux/README.md b/src/redux/README.md
index 5826f2ee4..e5a861496 100644
--- a/src/redux/README.md
+++ b/src/redux/README.md
@@ -19,6 +19,8 @@
MultiActionMiddleware
+PromiseMiddleware
+
StatusMiddleware
AppReducer
@@ -1062,6 +1064,82 @@ Allow passing an array of actions for batch dispatch.
+
+
+## PromiseMiddleware
+
+* [PromiseMiddleware](#Middleware.module_PromiseMiddleware)
+ * [~ActionType](#Middleware.module_PromiseMiddleware..ActionType) : Object
+ * [~createPromise(config)](#Middleware.module_PromiseMiddleware..createPromise) ⇒ function
+ * [~promiseMiddleware(config)](#Middleware.module_PromiseMiddleware..promiseMiddleware) ⇒ function
+
+
+
+### PromiseMiddleware~ActionType : Object
+Redux default action types for promiseMiddleware
+
+**Kind**: inner constant of [PromiseMiddleware
](#Middleware.module_PromiseMiddleware)
+
+
+### PromiseMiddleware~createPromise(config) ⇒ function
+Function: createPromise
+Description: The main createPromise accepts a configuration
+object and returns the middleware.
+
+**Kind**: inner method of [PromiseMiddleware
](#Middleware.module_PromiseMiddleware)
+
+
+
+ Param | Type |
+
+
+
+
+ config | object |
+
+ config.isCatchRejection | boolean |
+
+ config.promiseTypeDelimiter | string |
+
+ config.promiseTypeSuffixPending | string |
+
+ config.promiseTypeSuffixFulfilled | string |
+
+ config.promiseTypeSuffixRejected | string |
+
+
+
+
+
+### PromiseMiddleware~promiseMiddleware(config) ⇒ function
+Promise middleware
+Base code, https://github.com/pburtchaell/redux-promise-middleware
+Modified to allow configuration and "isCatchRejection".
+
+**Kind**: inner method of [PromiseMiddleware
](#Middleware.module_PromiseMiddleware)
+
+
+
+ Param | Type | Description |
+
+
+
+
+ config | object | |
+
+ config.isCatchRejection | boolean | Catch the returned promise. Helps avoid the "[uncaught in promise]" error
+ |
+
+ config.promiseTypeDelimiter | string | |
+
+ config.promiseTypeSuffixPending | string | |
+
+ config.promiseTypeSuffixFulfilled | string | |
+
+ config.promiseTypeSuffixRejected | string | |
+
+
+
## StatusMiddleware
diff --git a/src/redux/actions/__tests__/platformActions.test.js b/src/redux/actions/__tests__/platformActions.test.js
index 35631bce5..60c25c29c 100644
--- a/src/redux/actions/__tests__/platformActions.test.js
+++ b/src/redux/actions/__tests__/platformActions.test.js
@@ -5,7 +5,7 @@ import { platformActions } from '../platformActions';
import { appReducer } from '../../reducers';
describe('PlatformActions', () => {
- const middleware = [promiseMiddleware];
+ const middleware = [promiseMiddleware()];
const generateStore = () =>
createStore(
combineReducers({
diff --git a/src/redux/actions/__tests__/rhsmActions.test.js b/src/redux/actions/__tests__/rhsmActions.test.js
index af41b7fe1..a4338cc6a 100644
--- a/src/redux/actions/__tests__/rhsmActions.test.js
+++ b/src/redux/actions/__tests__/rhsmActions.test.js
@@ -6,7 +6,7 @@ import { rhsmConstants } from '../../../services/rhsm/rhsmConstants';
import { rhsmActions } from '../rhsmActions';
describe('RhsmActions', () => {
- const middleware = [multiActionMiddleware, promiseMiddleware];
+ const middleware = [multiActionMiddleware, promiseMiddleware()];
const generateStore = () =>
createStore(
combineReducers({
diff --git a/src/redux/actions/__tests__/userActions.test.js b/src/redux/actions/__tests__/userActions.test.js
index 1a0241900..f93a2f1f4 100644
--- a/src/redux/actions/__tests__/userActions.test.js
+++ b/src/redux/actions/__tests__/userActions.test.js
@@ -5,7 +5,7 @@ import { appReducer } from '../../reducers';
import { userActions } from '../userActions';
describe('UserActions', () => {
- const middleware = [promiseMiddleware];
+ const middleware = [promiseMiddleware()];
const generateStore = () =>
createStore(
combineReducers({
diff --git a/src/redux/middleware/index.js b/src/redux/middleware/index.js
index a74dccd1c..5c10897dd 100644
--- a/src/redux/middleware/index.js
+++ b/src/redux/middleware/index.js
@@ -1,7 +1,7 @@
import { createLogger } from 'redux-logger';
-import promiseMiddleware from 'redux-promise-middleware';
import { thunk as thunkMiddleware } from 'redux-thunk';
import { notificationsMiddleware } from '@redhat-cloud-services/frontend-components-notifications';
+import { promiseMiddleware } from './promiseMiddleware';
import { multiActionMiddleware } from './multiActionMiddleware';
import { statusMiddleware } from './statusMiddleware';
import { actionRecordMiddleware } from './actionRecordMiddleware';
@@ -34,7 +34,7 @@ const reduxMiddleware = [
thunkMiddleware,
statusMiddleware(),
multiActionMiddleware,
- promiseMiddleware,
+ promiseMiddleware({ isCatchRejection: true }),
actionRecordMiddleware({
id: process.env.REACT_APP_UI_LOGGER_ID,
app: { version: process.env.REACT_APP_UI_VERSION }
diff --git a/src/redux/middleware/promiseMiddleware.js b/src/redux/middleware/promiseMiddleware.js
new file mode 100644
index 000000000..7e6603030
--- /dev/null
+++ b/src/redux/middleware/promiseMiddleware.js
@@ -0,0 +1,230 @@
+import { helpers } from '../../common/helpers';
+
+/**
+ * @memberof Middleware
+ * @module PromiseMiddleware
+ */
+
+/**
+ * Redux default action types for promiseMiddleware
+ *
+ * @type {{Fulfilled: string, Rejected: string, Pending: string}}
+ */
+const ActionType = {
+ Pending: 'PENDING',
+ Fulfilled: 'FULFILLED',
+ Rejected: 'REJECTED'
+};
+
+/**
+ * Function: createPromise
+ * Description: The main createPromise accepts a configuration
+ * object and returns the middleware.
+ *
+ * @param {object} config
+ * @param {boolean} config.isCatchRejection
+ * @param {string} config.promiseTypeDelimiter
+ * @param {string} config.promiseTypeSuffixPending
+ * @param {string} config.promiseTypeSuffixFulfilled
+ * @param {string} config.promiseTypeSuffixRejected
+ * @returns {Function}
+ */
+const createPromise = ({
+ promiseTypeDelimiter: PROMISE_TYPE_DELIMITER = '_',
+ promiseTypeSuffixPending = ActionType.Pending,
+ promiseTypeSuffixFulfilled = ActionType.Fulfilled,
+ promiseTypeSuffixRejected = ActionType.Rejected,
+ isCatchRejection = false
+} = {}) => {
+ const PROMISE_TYPE_SUFFIXES = [promiseTypeSuffixPending, promiseTypeSuffixFulfilled, promiseTypeSuffixRejected];
+ return ref => {
+ const { dispatch } = ref;
+
+ return next => action => {
+ /**
+ * Instantiate variables to hold:
+ * (1) the promise
+ * (2) the data for optimistic updates
+ */
+ let promise;
+ let data;
+
+ /**
+ * There are multiple ways to dispatch a promise. The first step is to
+ * determine if the promise is defined:
+ * (a) explicitly (action.payload.promise is the promise)
+ * (b) implicitly (action.payload is the promise)
+ * (c) as an async function (returns a promise when called)
+ *
+ * If the promise is not defined in one of these three ways, we don't do
+ * anything and move on to the next middleware in the middleware chain.
+ */
+
+ // Step 1a: Is there a payload?
+ if (action.payload) {
+ const PAYLOAD = action.payload;
+
+ // Step 1.1: Is the promise implicitly defined?
+ if (helpers.isPromise(PAYLOAD)) {
+ promise = PAYLOAD;
+ }
+
+ // Step 1.2: Is the promise explicitly defined?
+ else if (helpers.isPromise(PAYLOAD.promise)) {
+ promise = PAYLOAD.promise;
+ data = PAYLOAD.data;
+ }
+
+ // Step 1.3: Is the promise returned by an async function?
+ else if (typeof PAYLOAD === 'function' || typeof PAYLOAD.promise === 'function') {
+ promise = PAYLOAD.promise ? PAYLOAD.promise() : PAYLOAD();
+ data = PAYLOAD.promise ? PAYLOAD.data : undefined;
+
+ // Step 1.3.1: Is the return of action.payload a promise?
+ if (!helpers.isPromise(promise)) {
+ // If not, move on to the next middleware.
+ return next({
+ ...action,
+ payload: promise
+ });
+ }
+ }
+
+ // Step 1.4: If there's no promise, move on to the next middleware.
+ else {
+ return next(action);
+ }
+
+ // Step 1b: If there's no payload, move on to the next middleware.
+ } else {
+ return next(action);
+ }
+
+ /**
+ * Instantiate and define constants for:
+ * (1) the action type
+ * (2) the action meta
+ */
+ const TYPE = action.type;
+ const META = action.meta;
+
+ /**
+ * Instantiate and define constants for the action type suffixes.
+ * These are appended to the end of the action type.
+ */
+ const [PENDING, FULFILLED, REJECTED] = PROMISE_TYPE_SUFFIXES;
+
+ /**
+ * Function: getAction
+ * Description: This function constructs and returns a rejected
+ * or fulfilled action object. The action object is based off the Flux
+ * Standard Action (FSA).
+ *
+ * Given an original action with the type FOO:
+ *
+ * The rejected object model will be:
+ * {
+ * error: true,
+ * type: 'FOO_REJECTED',
+ * payload: ...,
+ * meta: ... (optional)
+ * }
+ *
+ * The fulfilled object model will be:
+ * {
+ * type: 'FOO_FULFILLED',
+ * payload: ...,
+ * meta: ... (optional)
+ * }
+ *
+ * @param {unknown} newPayload
+ * @param {boolean} isRejected
+ * @returns {{payload: any, meta: any, type: string, error: any}}
+ */
+ const getAction = (newPayload, isRejected) => ({
+ // Concatenate the type string property.
+ type: [TYPE, isRejected ? REJECTED : FULFILLED].join(PROMISE_TYPE_DELIMITER),
+
+ // Include the payload property.
+ ...(newPayload === null || typeof newPayload === 'undefined'
+ ? {}
+ : {
+ payload: newPayload
+ }),
+
+ // If the original action includes a meta property, include it.
+ ...(META !== undefined ? { meta: META } : {}),
+
+ // If the action is rejected, include an error property.
+ ...(isRejected
+ ? {
+ error: true
+ }
+ : {})
+ });
+
+ const handleReject = reason => {
+ const rejectedAction = getAction(reason, true);
+ dispatch(rejectedAction);
+
+ if (isCatchRejection === false) {
+ throw reason;
+ }
+ };
+
+ const handleFulfill = (value = null) => {
+ const resolvedAction = getAction(value, false);
+ dispatch(resolvedAction);
+
+ return { value, action: resolvedAction };
+ };
+
+ /**
+ * First, dispatch the pending action:
+ * This object describes the pending state of a promise and will include
+ * any data (for optimistic updates) and/or meta from the original action.
+ */
+ next({
+ // Concatenate the type string.
+ type: [TYPE, PENDING].join(PROMISE_TYPE_DELIMITER),
+
+ // Include payload (for optimistic updates) if it is defined.
+ ...(data !== undefined ? { payload: data } : {}),
+
+ // Include meta data if it is defined.
+ ...(META !== undefined ? { meta: META } : {})
+ });
+
+ /**
+ * Second, dispatch a rejected or fulfilled action and move on to the
+ * next middleware.
+ */
+ return promise.then(handleFulfill, handleReject);
+ };
+ };
+};
+
+/**
+ * Promise middleware
+ * Base code, https://github.com/pburtchaell/redux-promise-middleware
+ * Modified to allow configuration and "isCatchRejection".
+ *
+ * @param {object} config
+ * @param {boolean} config.isCatchRejection Catch the returned promise. Helps avoid the "[uncaught in promise]" error
+ * @param {string} config.promiseTypeDelimiter
+ * @param {string} config.promiseTypeSuffixPending
+ * @param {string} config.promiseTypeSuffixFulfilled
+ * @param {string} config.promiseTypeSuffixRejected
+ * @returns {Function}
+ */
+const promiseMiddleware =
+ config =>
+ ({ dispatch } = {}) => {
+ if (typeof dispatch === 'function') {
+ return createPromise(config)({ dispatch });
+ }
+
+ return null;
+ };
+
+export { promiseMiddleware as default, promiseMiddleware, createPromise, ActionType };
diff --git a/tests/__snapshots__/dist.test.js.snap b/tests/__snapshots__/dist.test.js.snap
index ae644ca9f..25543700c 100644
--- a/tests/__snapshots__/dist.test.js.snap
+++ b/tests/__snapshots__/dist.test.js.snap
@@ -772,8 +772,6 @@ exports[`Build distribution should match a specific file output 1`] = `
"./dist/js/2438*txt",
"./dist/js/2471*js",
"./dist/js/2531*js",
- "./dist/js/254*js",
- "./dist/js/254*txt",
"./dist/js/2745*js",
"./dist/js/2871*js",
"./dist/js/2901*js",
@@ -822,6 +820,8 @@ exports[`Build distribution should match a specific file output 1`] = `
"./dist/js/5523*js",
"./dist/js/5600*js",
"./dist/js/5606*js",
+ "./dist/js/5683*js",
+ "./dist/js/5683*txt",
"./dist/js/5687*js",
"./dist/js/5717*js",
"./dist/js/5927*js",
@@ -906,7 +906,6 @@ exports[`Build distribution should match a specific file output 1`] = `
"./dist/sourcemaps/2438*map",
"./dist/sourcemaps/2471*map",
"./dist/sourcemaps/2531*map",
- "./dist/sourcemaps/254*map",
"./dist/sourcemaps/2745*map",
"./dist/sourcemaps/2871*map",
"./dist/sourcemaps/2901*map",
@@ -948,6 +947,7 @@ exports[`Build distribution should match a specific file output 1`] = `
"./dist/sourcemaps/5523*map",
"./dist/sourcemaps/5600*map",
"./dist/sourcemaps/5606*map",
+ "./dist/sourcemaps/5683*map",
"./dist/sourcemaps/5687*map",
"./dist/sourcemaps/5717*map",
"./dist/sourcemaps/5927*map",