From 6b05ebf54ea2d2bfdd61c3339bd655eb270cda13 Mon Sep 17 00:00:00 2001 From: CD Cabrera Date: Thu, 20 Aug 2020 12:37:12 -0400 Subject: [PATCH] fix(apiQueries,reduxHelpers): issues/380 query schema (#381) * apiQueries, reduxHelpers, parse queries for specific API endpoints * openshiftView, rhelView, apply parseRhsmQuery --- package.json | 3 +- .../__snapshots__/openshiftView.test.js.snap | 9 ------ src/components/openshiftView/openshiftView.js | 11 +++---- .../__snapshots__/rhelView.test.js.snap | 9 ------ src/components/rhelView/rhelView.js | 11 +++---- .../__snapshots__/apiQueries.test.js.snap | 29 +++++++++++++++++++ .../__snapshots__/reduxHelpers.test.js.snap | 7 +++++ src/redux/common/__tests__/apiQueries.test.js | 20 +++++++++++++ .../common/__tests__/reduxHelpers.test.js | 14 +++++++++ src/redux/common/apiQueries.js | 26 +++++++++++++++++ src/redux/common/index.js | 4 +++ src/redux/common/reduxHelpers.js | 28 +++++++++++++++++- src/redux/index.js | 13 ++++++++- src/redux/selectors/viewSelectors.js | 12 ++++++++ tests/__snapshots__/code.test.js.snap | 4 +-- 15 files changed, 167 insertions(+), 33 deletions(-) create mode 100644 src/redux/common/__tests__/__snapshots__/apiQueries.test.js.snap create mode 100644 src/redux/common/__tests__/apiQueries.test.js create mode 100644 src/redux/common/apiQueries.js create mode 100644 src/redux/common/index.js diff --git a/package.json b/package.json index 66242f8d5..203744c80 100644 --- a/package.json +++ b/package.json @@ -33,11 +33,12 @@ "!src/index.js", "!src/setupTests.js", "!src/components/app.js", - "!src/helpers/index.js", + "!src/common/index.js", "!src/redux/index.js", "!src/redux/store.js", "!src/redux/middleware/**", "!src/redux/actions/index.js", + "!src/redux/common/index.js", "!src/redux/reducers/index.js", "!src/redux/selectors/index.js" ], diff --git a/src/components/openshiftView/__tests__/__snapshots__/openshiftView.test.js.snap b/src/components/openshiftView/__tests__/__snapshots__/openshiftView.test.js.snap index 9f51f4a3e..201943ca7 100644 --- a/src/components/openshiftView/__tests__/__snapshots__/openshiftView.test.js.snap +++ b/src/components/openshiftView/__tests__/__snapshots__/openshiftView.test.js.snap @@ -51,8 +51,6 @@ exports[`OpenshiftView Component should display an alternate graph on query-stri query={ Object { "granularity": "daily", - "limit": 10, - "offset": 0, } } viewId="OpenShift" @@ -110,7 +108,6 @@ exports[`OpenshiftView Component should display an alternate graph on query-stri productId="lorem ipsum" query={ Object { - "granularity": "daily", "limit": 10, "offset": 0, } @@ -172,8 +169,6 @@ exports[`OpenshiftView Component should have a fallback title: title 1`] = ` query={ Object { "granularity": "daily", - "limit": 10, - "offset": 0, } } viewId="OpenShift" @@ -231,7 +226,6 @@ exports[`OpenshiftView Component should have a fallback title: title 1`] = ` productId="lorem ipsum" query={ Object { - "granularity": "daily", "limit": 10, "offset": 0, } @@ -413,8 +407,6 @@ exports[`OpenshiftView Component should render a non-connected component: non-co query={ Object { "granularity": "daily", - "limit": 10, - "offset": 0, } } viewId="OpenShift" @@ -472,7 +464,6 @@ exports[`OpenshiftView Component should render a non-connected component: non-co productId="lorem ipsum" query={ Object { - "granularity": "daily", "limit": 10, "offset": 0, } diff --git a/src/components/openshiftView/openshiftView.js b/src/components/openshiftView/openshiftView.js index 45eccff5a..af310386c 100644 --- a/src/components/openshiftView/openshiftView.js +++ b/src/components/openshiftView/openshiftView.js @@ -7,7 +7,7 @@ import { import { Badge, Button } from '@patternfly/react-core'; import { PageLayout, PageHeader, PageSection, PageToolbar } from '../pageLayout/pageLayout'; import { RHSM_API_QUERY_GRANULARITY_TYPES as GRANULARITY_TYPES, RHSM_API_QUERY_TYPES } from '../../types/rhsmApiTypes'; -import { connect, reduxSelectors } from '../../redux'; +import { apiQueries, connect, reduxSelectors } from '../../redux'; import GraphCard from '../graphCard/graphCard'; import C3GraphCard from '../c3GraphCard/c3GraphCard'; import { Select } from '../form/select'; @@ -89,6 +89,7 @@ class OpenshiftView extends React.Component { const { graphFilters, inventoryFilters } = this.state; const { query, initialToolbarFilters, location, routeDetail, t, viewId } = this.props; const isC3 = location?.parsedSearch?.c3 === ''; + const { graphQuery, inventoryQuery, toolbarQuery } = apiQueries.parseRhsmQuery(query); return ( @@ -96,14 +97,14 @@ class OpenshiftView extends React.Component { {t(`curiosity-view.title`, { appName: helpers.UI_DISPLAY_NAME, context: viewId })} - + {(isC3 && ( @@ -51,14 +52,14 @@ class RhelView extends React.Component { {t(`curiosity-view.title`, { appName: helpers.UI_DISPLAY_NAME, context: viewId })} - + {(isC3 && ( { + it('should have specific functions', () => { + expect(apiQueries).toMatchSnapshot('apiQueries'); + }); + + it('should parse a query object into specific api facets', () => { + const rhsmApiQuery = { + [RHSM_API_QUERY_TYPES.GRANULARITY]: GRANULARITY_TYPES.DAILY, + [RHSM_API_QUERY_TYPES.LIMIT]: 10, + [RHSM_API_QUERY_TYPES.OFFSET]: 0 + }; + expect(apiQueries.parseRhsmQuery(rhsmApiQuery)).toMatchSnapshot('rhsm'); + }); +}); diff --git a/src/redux/common/__tests__/reduxHelpers.test.js b/src/redux/common/__tests__/reduxHelpers.test.js index 8b194705f..1e50c4252 100644 --- a/src/redux/common/__tests__/reduxHelpers.test.js +++ b/src/redux/common/__tests__/reduxHelpers.test.js @@ -1,4 +1,9 @@ import { reduxHelpers } from '../reduxHelpers'; +import { + RHSM_API_QUERY_GRANULARITY_TYPES as GRANULARITY_TYPES, + RHSM_API_QUERY_TYPES, + RHSM_API_QUERY_SET_REPORT_CAPACITY_TYPES +} from '../../../types/rhsmApiTypes'; describe('ReduxHelpers', () => { it('should have specific functions', () => { @@ -12,6 +17,15 @@ describe('ReduxHelpers', () => { expect(reduxHelpers.HTTP_STATUS_RANGE('lorem')).toBe('lorem_STATUS_RANGE'); }); + it('should generate an expected API query with an existing schema', () => { + const rhsmQuery = { + [RHSM_API_QUERY_TYPES.GRANULARITY]: GRANULARITY_TYPES.DAILY, + [RHSM_API_QUERY_TYPES.LIMIT]: 10, + [RHSM_API_QUERY_TYPES.OFFSET]: 0 + }; + expect(reduxHelpers.setApiQuery(rhsmQuery, RHSM_API_QUERY_SET_REPORT_CAPACITY_TYPES)).toMatchSnapshot('rhsm query'); + }); + it('should generate an expected API response with an existing schema', () => { expect(reduxHelpers.setResponseSchemas([{ LOREM: 'ipsum' }, { HELLO: 'world' }])).toMatchSnapshot('object'); expect( diff --git a/src/redux/common/apiQueries.js b/src/redux/common/apiQueries.js new file mode 100644 index 000000000..656370211 --- /dev/null +++ b/src/redux/common/apiQueries.js @@ -0,0 +1,26 @@ +import { rhsmApiTypes } from '../../types/rhsmApiTypes'; +import { reduxHelpers } from './reduxHelpers'; + +/** + * Parse a query object against a schema for specific RHSM endpoints. + * + * @param {object} query + * @returns {{query: {}, graphQuery: {}, inventoryQuery: {}, toolbarQuery: {}}} + */ +const parseRhsmQuery = (query = {}) => { + const graphQuery = reduxHelpers.setApiQuery(query, rhsmApiTypes.RHSM_API_QUERY_SET_REPORT_CAPACITY_TYPES); + const inventoryQuery = reduxHelpers.setApiQuery(query, rhsmApiTypes.RHSM_API_QUERY_SET_INVENTORY_TYPES); + + return { + query, + graphQuery, + inventoryQuery, + toolbarQuery: query + }; +}; + +const apiQueries = { + parseRhsmQuery +}; + +export { apiQueries as default, apiQueries }; diff --git a/src/redux/common/index.js b/src/redux/common/index.js new file mode 100644 index 000000000..8f4132fd7 --- /dev/null +++ b/src/redux/common/index.js @@ -0,0 +1,4 @@ +import { apiQueries } from './apiQueries'; +import { reduxHelpers } from './reduxHelpers'; + +export { reduxHelpers as default, reduxHelpers, apiQueries }; diff --git a/src/redux/common/reduxHelpers.js b/src/redux/common/reduxHelpers.js index 3107dc0ab..81307007f 100644 --- a/src/redux/common/reduxHelpers.js +++ b/src/redux/common/reduxHelpers.js @@ -35,6 +35,31 @@ const REJECTED_ACTION = (base = '') => `${base}_REJECTED`; */ const HTTP_STATUS_RANGE = status => `${status}_STATUS_RANGE`; +/** + * Set an API query based on specific API "acceptable values" schema. + * + * @param {object} values + * @param {object} schema + * @param {*} [initialValue] + * @returns {object} + */ +const setApiQuery = (values, schema, initialValue) => { + const generated = {}; + const schemaArr = (schema && Object.values(schema)) || []; + + schemaArr.forEach(value => { + if (initialValue === undefined) { + if (value in values) { + generated[value] = values?.[value]; + } + } else { + generated[value] = values?.[value] || initialValue; + } + }); + + return generated; +}; + // ToDo: research applying a maintained schema map/normalizer such as, normalizr /** * Apply a set of schemas using either an array of objects in the @@ -42,7 +67,7 @@ const HTTP_STATUS_RANGE = status => `${status}_STATUS_RANGE`; * in the form of [['some_api_key','another_api_key']] * * @param {Array} schemas - * @param {*} initialValue + * @param {*} [initialValue] * @returns {Array} */ const setResponseSchemas = (schemas = [], initialValue) => @@ -392,6 +417,7 @@ const reduxHelpers = { PENDING_ACTION, REJECTED_ACTION, HTTP_STATUS_RANGE, + setApiQuery, setResponseSchemas, setNormalizedResponse, generatedPromiseActionReducer, diff --git a/src/redux/index.js b/src/redux/index.js index f527bc353..775441b7e 100644 --- a/src/redux/index.js +++ b/src/redux/index.js @@ -2,6 +2,7 @@ import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import { store } from './store'; import { reduxActions } from './actions'; +import { reduxHelpers, apiQueries } from './common'; import { reduxReducers } from './reducers'; import { reduxSelectors } from './selectors'; import { reduxTypes } from './types'; @@ -9,4 +10,14 @@ import { reduxTypes } from './types'; const connectRouter = (mapStateToProps, mapDispatchToProps) => component => withRouter(connect(mapStateToProps, mapDispatchToProps)(component)); -export { connect, connectRouter, reduxActions, reduxReducers, reduxSelectors, reduxTypes, store }; +export { + apiQueries, + connect, + connectRouter, + reduxActions, + reduxHelpers, + reduxReducers, + reduxSelectors, + reduxTypes, + store +}; diff --git a/src/redux/selectors/viewSelectors.js b/src/redux/selectors/viewSelectors.js index cef50b858..26cdb9f14 100644 --- a/src/redux/selectors/viewSelectors.js +++ b/src/redux/selectors/viewSelectors.js @@ -9,6 +9,18 @@ import _isEqual from 'lodash/isEqual'; */ const createDeepEqualSelector = createSelectorCreator(defaultMemoize, _isEqual); +/** + * ToDo: as part of future potential upgrades consider moving reduxHelpers.setApiQuery into statePropsFilter + * The side effect is multiple queries being dumped into the views, meaning more "configuration to handle". + * Leaving it as a single query passed from this selector and handling the "setApiQuery" in "redux/common/apiQueries" + * is a first step. + * i.e. + * queries: { + * query: ..., + * graph: reduxHelpers.setApiQuery(..., RHSM_API_QUERY_SET_REPORT_CAPACITY_TYPES), + * inventory: reduxHelpers.setApiQuery(..., RHSM_API_QUERY_SET_INVENTORY_TYPES) + * } + */ /** * Return a combined state, props object. * diff --git a/tests/__snapshots__/code.test.js.snap b/tests/__snapshots__/code.test.js.snap index 53619333c..6dd867ac6 100644 --- a/tests/__snapshots__/code.test.js.snap +++ b/tests/__snapshots__/code.test.js.snap @@ -2,8 +2,8 @@ exports[`General code checks should only have specific console.[warn|log|info|error] methods: console methods 1`] = ` Array [ - "redux/common/reduxHelpers.js:225: console.error(\`Error: Property \${prop} does not exist within the passed state.\`, state);", - "redux/common/reduxHelpers.js:229: console.warn(\`Warning: Property \${prop} does not exist within the passed initialState.\`, initialState);", + "redux/common/reduxHelpers.js:250: console.error(\`Error: Property \${prop} does not exist within the passed state.\`, state);", + "redux/common/reduxHelpers.js:254: console.warn(\`Warning: Property \${prop} does not exist within the passed initialState.\`, initialState);", "setupTests.js:70: console.error = (message, ...args) => {", ] `;