From 628f57eba704323ebe39214d6d358520157ec00c Mon Sep 17 00:00:00 2001 From: CD Cabrera Date: Tue, 31 Jan 2023 13:36:24 -0500 Subject: [PATCH] refactor(config): sw-625 generate routes (#1045) * authentication, redirect errorRoute ref * build, spelling, jest, local run nav * config, generate products, routes * productView, replaced routerHelpers array ref * productViewMissing, updated routerHelpers * redirect, simplify, prep router npm update * redux, viewReducer, updated routerHelpers * router, prep router npm update * routerHelpers, refactor, removed unused funcs --- config/cspell.config.json | 1 + config/jest.setupTests.js | 15 +- .../__snapshots__/authentication.test.js.snap | 261 +- .../authenticationContext.test.js.snap | 32 + .../__tests__/authenticationContext.test.js | 7 +- .../authentication/authentication.js | 2 +- .../__tests__/__snapshots__/i18n.test.js.snap | 6 +- .../productViewContext.test.js.snap | 18 +- .../productViewMissing.test.js.snap | 109 +- .../__tests__/productViewMissing.test.js | 38 +- src/components/productView/productView.js | 5 +- .../productView/productViewMissing.js | 23 +- .../__snapshots__/redirect.test.js.snap | 446 +--- .../__snapshots__/router.test.js.snap | 2365 ++++++++++++++++- .../__snapshots__/routerHelpers.test.js.snap | 245 +- .../router/__tests__/redirect.test.js | 28 +- .../router/__tests__/router.test.js | 14 + .../router/__tests__/routerHelpers.test.js | 126 +- src/components/router/redirect.js | 39 +- src/components/router/router.js | 23 +- src/components/router/routerHelpers.js | 329 +-- .../__snapshots__/index.test.js.snap | 39 +- .../product.openshiftContainer.test.js.snap | 226 +- .../__snapshots__/product.rhacs.test.js.snap | 4 +- .../__snapshots__/product.rhel.test.js.snap | 499 +++- .../__snapshots__/product.rhods.test.js.snap | 4 +- .../__snapshots__/product.rhosak.test.js.snap | 4 +- .../product.satellite.test.js.snap | 184 +- .../__snapshots__/products.test.js.snap | 137 + .../__snapshots__/routes.test.js.snap | 21 + src/config/__tests__/index.test.js | 26 +- .../product.openshiftContainer.test.js | 33 +- src/config/__tests__/product.rhacs.test.js | 2 +- src/config/__tests__/product.rhel.test.js | 35 +- src/config/__tests__/product.rhods.test.js | 2 +- src/config/__tests__/product.rhosak.test.js | 2 +- .../__tests__/product.satellite.test.js | 23 +- src/config/__tests__/products.test.js | 37 + src/config/__tests__/routes.test.js | 7 + src/config/index.js | 20 +- src/config/product.openshiftContainer.js | 4 +- src/config/product.openshiftDedicated.js | 2 + src/config/product.openshiftMetrics.js | 6 +- src/config/product.rhacs.js | 2 + src/config/product.rhel.js | 12 +- src/config/product.rhods.js | 2 + src/config/product.rhosak.js | 2 + src/config/product.satellite.js | 12 +- src/config/products.js | 89 + src/config/routes.js | 140 +- .../__snapshots__/useRouter.test.js.snap | 29 +- src/hooks/__tests__/useRouter.test.js | 24 +- src/hooks/useRouter.js | 23 +- src/index.js | 5 +- src/redux/reducers/viewReducer.js | 8 +- tests/__snapshots__/code.test.js.snap | 1 + tests/__snapshots__/dist.test.js.snap | 57 +- 57 files changed, 4025 insertions(+), 1830 deletions(-) create mode 100644 src/config/__tests__/__snapshots__/products.test.js.snap create mode 100644 src/config/__tests__/__snapshots__/routes.test.js.snap create mode 100644 src/config/__tests__/products.test.js create mode 100644 src/config/__tests__/routes.test.js create mode 100644 src/config/products.js diff --git a/config/cspell.config.json b/config/cspell.config.json index c0631e649..47e36bb34 100644 --- a/config/cspell.config.json +++ b/config/cspell.config.json @@ -23,6 +23,7 @@ "hoverable", "ibmpower", "ibmz", + "iife", "ipsum", "keycloak", "kubernetes", diff --git a/config/jest.setupTests.js b/config/jest.setupTests.js index dd6cee87b..367c805da 100644 --- a/config/jest.setupTests.js +++ b/config/jest.setupTests.js @@ -258,10 +258,7 @@ global.mockWindowLocation = async ( { url = 'https://ci.foo.redhat.com/subscriptions/rhel', location: locationProps = {} } = {} ) => { const updatedUrl = new URL(url); - const { location } = window; - delete window.location; - // mock - window.location = { + const updatedLocation = { href: updatedUrl.href, search: updatedUrl.search, hash: updatedUrl.hash, @@ -269,9 +266,13 @@ global.mockWindowLocation = async ( replace: Function.prototype, ...locationProps }; - await callback(window.location); - // restore - window.location = location; + + const { mockClear } = mockObjectProperty(window, 'location', updatedLocation); + await callback(updatedLocation); + + return { + mockClear + }; }; // FixMe: revisit squashing log and group messaging, redux leaks log messaging diff --git a/src/components/authentication/__tests__/__snapshots__/authentication.test.js.snap b/src/components/authentication/__tests__/__snapshots__/authentication.test.js.snap index cfd49a79a..c847804a1 100644 --- a/src/components/authentication/__tests__/__snapshots__/authentication.test.js.snap +++ b/src/components/authentication/__tests__/__snapshots__/authentication.test.js.snap @@ -318,93 +318,8 @@ exports[`Authentication Component should return a redirect on 418 error: 418 err } > @@ -423,93 +338,8 @@ exports[`Authentication Component should return a redirect on a specific 403 err } > @@ -528,93 +358,8 @@ exports[`Authentication Component should return a redirect on a specific 403 err } > diff --git a/src/components/authentication/__tests__/__snapshots__/authenticationContext.test.js.snap b/src/components/authentication/__tests__/__snapshots__/authenticationContext.test.js.snap index b65956c25..6187881c6 100644 --- a/src/components/authentication/__tests__/__snapshots__/authenticationContext.test.js.snap +++ b/src/components/authentication/__tests__/__snapshots__/authenticationContext.test.js.snap @@ -63,6 +63,38 @@ exports[`AuthenticationContext should apply a hook for retrieving auth data from } `; +exports[`AuthenticationContext should apply a hook for retrieving auth data from multiple selectors: success dispatch 1`] = ` +[ + [ + [Function], + ], + [ + [ + { + "payload": Promise {}, + "type": "PLATFORM_INIT", + }, + { + "meta": { + "data": { + "name": "subscriptions", + }, + }, + "payload": Promise {}, + "type": "PLATFORM_APP_NAME", + }, + { + "payload": Promise {}, + "type": "PLATFORM_GLOBAL_FILTER_HIDE", + }, + ], + ], + [ + [Function], + ], +] +`; + exports[`AuthenticationContext should apply a hook for retrieving auth data from multiple selectors: success response 1`] = ` { "data": { diff --git a/src/components/authentication/__tests__/authenticationContext.test.js b/src/components/authentication/__tests__/authenticationContext.test.js index c0e1eebc5..8881fb31b 100644 --- a/src/components/authentication/__tests__/authenticationContext.test.js +++ b/src/components/authentication/__tests__/authenticationContext.test.js @@ -6,7 +6,7 @@ describe('AuthenticationContext', () => { expect(context).toMatchSnapshot('specific properties'); }); - it('should apply a hook for retrieving auth data from multiple selectors', () => { + it('should apply a hook for retrieving auth data from multiple selectors', async () => { const { result: errorResponse } = shallowHook(() => useGetAuthorization({ useSelectorsResponse: () => ({ @@ -29,8 +29,10 @@ describe('AuthenticationContext', () => { expect(errorResponse).toMatchSnapshot('error response'); - const { result: successResponse } = shallowHook(() => + const mockDispatch = jest.fn(); + const { result: successResponse } = await mountHook(() => useGetAuthorization({ + useDispatch: () => mockDispatch, useSelectorsResponse: () => ({ fulfilled: true, data: { @@ -47,6 +49,7 @@ describe('AuthenticationContext', () => { }) ); + expect(mockDispatch.mock.calls).toMatchSnapshot('success dispatch'); expect(successResponse).toMatchSnapshot('success response'); const { result: mockStoreSuccessResponse } = shallowHook(() => useGetAuthorization(), { diff --git a/src/components/authentication/authentication.js b/src/components/authentication/authentication.js index ec33f8453..0fccced67 100644 --- a/src/components/authentication/authentication.js +++ b/src/components/authentication/authentication.js @@ -47,7 +47,7 @@ const Authentication = ({ appName, children, isDisabled, t, useGetAuthorization: (errorCodes && errorCodes.includes(rhsmConstants.RHSM_API_RESPONSE_ERRORS_CODE_TYPES.OPTIN)) || errorStatus === 418 ) { - return ; + return ; } return ( diff --git a/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap b/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap index 620c761fd..8c08fea02 100644 --- a/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap +++ b/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap @@ -277,7 +277,7 @@ exports[`I18n Component should generate a predictable locale key output snapshot }, { "key": "curiosity-view.title", - "match": "t(\`curiosity-view.title\`, { appName: helpers.UI_DISPLAY_NAME, context: routeProductLabel })", + "match": "t(\`curiosity-view.title\`, { appName: helpers.UI_DISPLAY_NAME, context: updatedRouteProductLabel })", }, ], }, @@ -290,11 +290,11 @@ exports[`I18n Component should generate a predictable locale key output snapshot }, { "key": "curiosity-view.title", - "match": "t('curiosity-view.title', { appName: helpers.UI_DISPLAY_NAME, context: (Array.isArray(product.pathParameter)", + "match": "t('curiosity-view.title', { appName: helpers.UI_DISPLAY_NAME, context: product.productId })", }, { "key": "curiosity-view.description", - "match": "t('curiosity-view.description', { appName: helpers.UI_DISPLAY_NAME, context: (Array.isArray(product.productParameter)", + "match": "t('curiosity-view.description', { appName: helpers.UI_DISPLAY_NAME, context: product.productId })", }, ], }, diff --git a/src/components/productView/__tests__/__snapshots__/productViewContext.test.js.snap b/src/components/productView/__tests__/__snapshots__/productViewContext.test.js.snap index b56a5b7b6..d08e8fd0b 100644 --- a/src/components/productView/__tests__/__snapshots__/productViewContext.test.js.snap +++ b/src/components/productView/__tests__/__snapshots__/productViewContext.test.js.snap @@ -24,6 +24,10 @@ exports[`ProductViewContext should apply a hook for retrieving product context: exports[`ProductViewContext should apply a hook for retrieving product context: product context, uom filtering cores 1`] = ` { + "aliases": [ + "OpenShift Container Platform", + "openshift", + ], "graphTallyQuery": { "granularity": "Daily", }, @@ -145,20 +149,25 @@ exports[`ProductViewContext should apply a hook for retrieving product context: "sort": "next_event_date", }, "productDisplay": "partial", - "productGroup": "OpenShift Container Platform", + "productGroup": "openshift-container", "productId": "OpenShift Container Platform", "productLabel": "OpenShift Container Platform", + "productPath": "openshift-container", "query": { "beginning": "2019-06-20T00:00:00.000Z", "ending": "2019-07-20T23:59:59.999Z", "uom": "cores", }, - "viewId": "viewOpenShift Container Platform", + "viewId": "viewopenshift-container", } `; exports[`ProductViewContext should apply a hook for retrieving product context: product context, uom filtering sockets 1`] = ` { + "aliases": [ + "OpenShift Container Platform", + "openshift", + ], "graphTallyQuery": { "granularity": "Daily", }, @@ -280,15 +289,16 @@ exports[`ProductViewContext should apply a hook for retrieving product context: "sort": "next_event_date", }, "productDisplay": "partial", - "productGroup": "OpenShift Container Platform", + "productGroup": "openshift-container", "productId": "OpenShift Container Platform", "productLabel": "OpenShift Container Platform", + "productPath": "openshift-container", "query": { "beginning": "2019-06-20T00:00:00.000Z", "ending": "2019-07-20T23:59:59.999Z", "uom": "cores", }, - "viewId": "viewOpenShift Container Platform", + "viewId": "viewopenshift-container", } `; diff --git a/src/components/productView/__tests__/__snapshots__/productViewMissing.test.js.snap b/src/components/productView/__tests__/__snapshots__/productViewMissing.test.js.snap index 70538a3cb..1ab5cd824 100644 --- a/src/components/productView/__tests__/__snapshots__/productViewMissing.test.js.snap +++ b/src/components/productView/__tests__/__snapshots__/productViewMissing.test.js.snap @@ -3,20 +3,12 @@ exports[`ProductViewMissing Component should redirect when there are limited product cards: redirect action 1`] = ` [ [ - { - "meta": { - "appName": undefined, - "id": "rhel", - "secondaryNav": undefined, - }, - "payload": Promise {}, - "type": "PLATFORM_SET_NAV", - }, + "openshift-container", ], ] `; -exports[`ProductViewMissing Component should render a non-connected component: non-connected 1`] = ` +exports[`ProductViewMissing Component should render a basic component: basic 1`] = ` @@ -34,43 +26,7 @@ exports[`ProductViewMissing Component should render a non-connected component: n > - - - t(curiosity-view.title, {"appName":"Subscriptions","context":"RHEL"}) - - - - t(curiosity-view.description, {"appName":"Subscriptions","context":"RHEL"}) - - - - - - @@ -106,7 +62,7 @@ exports[`ProductViewMissing Component should render a non-connected component: n @@ -142,7 +98,7 @@ exports[`ProductViewMissing Component should render a non-connected component: n @@ -150,13 +106,13 @@ exports[`ProductViewMissing Component should render a non-connected component: n headingLevel="h2" size="lg" > - t(curiosity-view.title, {"appName":"Subscriptions","context":"rhacs"}) + t(curiosity-view.title, {"appName":"Subscriptions","context":"OpenShift-metrics"}) - t(curiosity-view.description, {"appName":"Subscriptions","context":"rhacs"}) + t(curiosity-view.description, {"appName":"Subscriptions","context":"OpenShift-metrics"}) + + t(curiosity-inventory.label, {"context":"numberOfGuests","count":3}, [object Object]) + , + }, + { + "title": 100, + }, + { + "title": 20, + }, + { + "title": , + }, + ], + "columnHeaders": [ + { + "title": "t(curiosity-inventory.header_display, {"context":"name"})", + "transforms": [], + }, + { + "title": "t(curiosity-inventory.header, {"context":"sockets"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header, {"context":"cores"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_last, {"context":"seen"})", + "transforms": [ + [Function], + ], + }, + ], + "data": { + "cores": { + "title": "t(curiosity-inventory.header, {"context":"cores"})", + "value": 20, + }, + "display_name": { + "title": "t(curiosity-inventory.header_display, {"context":"name"})", + "value": "lorem", + }, + "inventory_id": { + "title": "t(curiosity-inventory.header_inventory, {"context":"id"})", + "value": "XXXX-XXXX-XXXXX-XXXXX", + }, + "last_seen": { + "title": "t(curiosity-inventory.header_last, {"context":"seen"})", + "value": "2022-01-01T00:00:00.000Z", + }, + "loremIpsum": { + "title": "t(curiosity-inventory.header, {"context":"loremIpsum"})", + "value": "hello world", + }, + "number_of_guests": { + "title": "t(curiosity-inventory.header_number_of, {"context":"guests"})", + "value": 3, + }, + "sockets": { + "title": "t(curiosity-inventory.header, {"context":"sockets"})", + "value": 100, + }, + }, +} +`; + exports[`Product OpenShift Container config should apply hosts inventory configuration: filtered, fallback display 1`] = ` { "cells": [ @@ -542,3 +682,77 @@ exports[`Product OpenShift Container config should apply subscriptions inventory }, } `; + +exports[`Product OpenShift Container config should apply subscriptions inventory configuration: filtered, infinite 1`] = ` +{ + "cells": [ + { + "title": "lorem", + }, + { + "title": "hello world", + }, + { + "title": "", + }, + { + "title": 2000, + }, + { + "title": "2022-01-01", + }, + ], + "columnHeaders": [ + { + "title": "t(curiosity-inventory.header_product, {"context":"name"})", + "transforms": [], + }, + { + "title": "t(curiosity-inventory.header_service, {"context":"level"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header, {"context":"quantity"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header, {"context":"subscriptions"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_next_event, {"context":"date"})", + "transforms": [ + [Function], + ], + }, + ], + "data": { + "has_infinite_quantity": { + "title": "t(curiosity-inventory.header_has_infinite, {"context":"quantity"})", + "value": false, + }, + "next_event_date": { + "title": "t(curiosity-inventory.header_next_event, {"context":"date"})", + "value": "2022-01-01T00:00:00.000Z", + }, + "product_name": { + "title": "t(curiosity-inventory.header_product, {"context":"name"})", + "value": "lorem", + }, + "service_level": { + "title": "t(curiosity-inventory.header_service, {"context":"level"})", + "value": "hello world", + }, + "total_capacity": { + "title": "t(curiosity-inventory.header_total, {"context":"capacity"})", + "value": 2000, + }, + }, +} +`; diff --git a/src/config/__tests__/__snapshots__/product.rhacs.test.js.snap b/src/config/__tests__/__snapshots__/product.rhacs.test.js.snap index 08799d4f8..32075dcca 100644 --- a/src/config/__tests__/__snapshots__/product.rhacs.test.js.snap +++ b/src/config/__tests__/__snapshots__/product.rhacs.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Product RHACS config should apply an instances inventory configuration under hosts: filtered 1`] = ` +exports[`Product RHACS config should apply an inventory configuration: filtered 1`] = ` { "cells": [ { @@ -59,7 +59,7 @@ exports[`Product RHACS config should apply an instances inventory configuration } `; -exports[`Product RHACS config should apply an instances inventory configuration under hosts: filtered, authorized 1`] = ` +exports[`Product RHACS config should apply an inventory configuration: filtered, authorized 1`] = ` { "cells": [ { diff --git a/src/config/__tests__/__snapshots__/product.rhel.test.js.snap b/src/config/__tests__/__snapshots__/product.rhel.test.js.snap index 8fa98992d..293ebcf69 100644 --- a/src/config/__tests__/__snapshots__/product.rhel.test.js.snap +++ b/src/config/__tests__/__snapshots__/product.rhel.test.js.snap @@ -1,5 +1,306 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Product RHEL config should apply an inventory configuration: filtered 1`] = ` +{ + "cells": [ + { + "title": "lorem", + }, + { + "title": 3, + }, + { + "title": + t(curiosity-inventory.label, {"context":"category"}) + + + , + }, + { + "title": "--", + }, + { + "title": , + }, + ], + "columnHeaders": [ + { + "title": "t(curiosity-inventory.header_display_name, {"context":"RHEL"})", + "transforms": [], + }, + { + "title": "t(curiosity-inventory.header_number_of_guests, {"context":"RHEL"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_category, {"context":"RHEL"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_Sockets, {"context":"RHEL"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_last_seen, {"context":"RHEL"})", + "transforms": [ + [Function], + ], + }, + ], + "data": { + "cores": { + "title": "t(curiosity-inventory.header_cores, {"context":"RHEL"})", + "value": 12, + }, + "display_name": { + "title": "t(curiosity-inventory.header_display_name, {"context":"RHEL"})", + "value": "lorem", + }, + "hardware_type": { + "title": "t(curiosity-inventory.header_hardware_type, {"context":"RHEL"})", + "value": "ipsum", + }, + "inventory_id": { + "title": "t(curiosity-inventory.header_inventory_id, {"context":"RHEL"})", + "value": "", + }, + "last_seen": { + "title": "t(curiosity-inventory.header_last_seen, {"context":"RHEL"})", + "value": "2022-01-01T00:00:00.000Z", + }, + "loremIpsum": { + "title": "t(curiosity-inventory.header_loremIpsum, {"context":"RHEL"})", + "value": "hello world", + }, + "measurement_type": { + "title": "t(curiosity-inventory.header_measurement_type, {"context":"RHEL"})", + "value": null, + }, + "number_of_guests": { + "title": "t(curiosity-inventory.header_number_of_guests, {"context":"RHEL"})", + "value": 3, + }, + "sockets": { + "title": "t(curiosity-inventory.header_sockets, {"context":"RHEL"})", + "value": 10, + }, + }, +} +`; + +exports[`Product RHEL config should apply an inventory configuration: filtered, authorized 1`] = ` +{ + "cells": [ + { + "title": , + }, + { + "title": 3, + }, + { + "title": + t(curiosity-inventory.label, {"context":"category"}) + + + , + }, + { + "title": "--", + }, + { + "title": , + }, + ], + "columnHeaders": [ + { + "title": "t(curiosity-inventory.header_display, {"context":"name"})", + "transforms": [], + }, + { + "title": "t(curiosity-inventory.header_number_of, {"context":"guests"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header, {"context":"category"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header, {"context":"Sockets"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_last, {"context":"seen"})", + "transforms": [ + [Function], + ], + }, + ], + "data": { + "cores": { + "title": "t(curiosity-inventory.header, {"context":"cores"})", + "value": 12, + }, + "display_name": { + "title": "t(curiosity-inventory.header_display, {"context":"name"})", + "value": "lorem", + }, + "hardware_type": { + "title": "t(curiosity-inventory.header_hardware, {"context":"type"})", + "value": "ipsum", + }, + "inventory_id": { + "title": "t(curiosity-inventory.header_inventory, {"context":"id"})", + "value": "XXXX-XXXX-XXXXX-XXXXX", + }, + "last_seen": { + "title": "t(curiosity-inventory.header_last, {"context":"seen"})", + "value": "2022-01-01T00:00:00.000Z", + }, + "loremIpsum": { + "title": "t(curiosity-inventory.header, {"context":"loremIpsum"})", + "value": "hello world", + }, + "measurement_type": { + "title": "t(curiosity-inventory.header_measurement, {"context":"type"})", + "value": null, + }, + "number_of_guests": { + "title": "t(curiosity-inventory.header_number_of, {"context":"guests"})", + "value": 3, + }, + "sockets": { + "title": "t(curiosity-inventory.header, {"context":"sockets"})", + "value": 10, + }, + }, +} +`; + +exports[`Product RHEL config should apply an inventory configuration: filtered, fallback display 1`] = ` +{ + "cells": [ + { + "title": "lorem", + }, + { + "title": 3, + }, + { + "title": + t(curiosity-inventory.label, {"context":"category"}) + + + , + }, + { + "title": "--", + }, + { + "title": "", + }, + ], + "columnHeaders": [ + { + "title": "t(curiosity-inventory.header_display_name, {"context":"RHEL"})", + "transforms": [], + }, + { + "title": "t(curiosity-inventory.header_number_of_guests, {"context":"RHEL"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_category, {"context":"RHEL"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_Sockets, {"context":"RHEL"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_last_seen, {"context":"RHEL"})", + "transforms": [ + [Function], + ], + }, + ], + "data": { + "cloud_provider": { + "title": "t(curiosity-inventory.header_cloud_provider, {"context":"RHEL"})", + "value": "dolor sit", + }, + "cores": { + "title": "t(curiosity-inventory.header_cores, {"context":"RHEL"})", + "value": 12, + }, + "display_name": { + "title": "t(curiosity-inventory.header_display_name, {"context":"RHEL"})", + "value": "lorem", + }, + "hardware_type": { + "title": "t(curiosity-inventory.header_hardware_type, {"context":"RHEL"})", + "value": "ipsum", + }, + "inventory_id": { + "title": "t(curiosity-inventory.header_inventory_id, {"context":"RHEL"})", + "value": null, + }, + "last_seen": { + "title": "t(curiosity-inventory.header_last_seen, {"context":"RHEL"})", + "value": null, + }, + "loremIpsum": { + "title": "t(curiosity-inventory.header_loremIpsum, {"context":"RHEL"})", + "value": "hello world", + }, + "measurement_type": { + "title": "t(curiosity-inventory.header_measurement_type, {"context":"RHEL"})", + "value": null, + }, + "number_of_guests": { + "title": "t(curiosity-inventory.header_number_of_guests, {"context":"RHEL"})", + "value": 3, + }, + "sockets": { + "title": "t(curiosity-inventory.header_sockets, {"context":"RHEL"})", + "value": 10, + }, + }, +} +`; + exports[`Product RHEL config should apply graph configuration: filters 1`] = ` { "groupedFiltersSettings": { @@ -230,24 +531,14 @@ exports[`Product RHEL config should apply guest inventory configuration: filtere } `; -exports[`Product RHEL config should apply hosts inventory configuration: filtered 1`] = ` +exports[`Product RHEL config should apply guest inventory configuration: filtered, missing inventory id 1`] = ` { "cells": [ { "title": "lorem", }, { - "title": 3, - }, - { - "title": - t(curiosity-inventory.label, {"context":"category"}) - - - , - }, - { - "title": "--", + "title": "", }, { "title": - t(curiosity-inventory.label, {"context":"category"}) - - - , + "title": "", }, { - "title": "--", + "title": + + , }, { - "title": "", + "title": "2022-01-01", }, ], "columnHeaders": [ { - "title": "t(curiosity-inventory.header_display, {"context":"name"})", + "title": "t(curiosity-inventory.header_product, {"context":"name"})", "transforms": [], }, { - "title": "t(curiosity-inventory.header_number_of, {"context":"guests"})", + "title": "t(curiosity-inventory.header_service, {"context":"level"})", "transforms": [ [Function], ], }, { - "title": "t(curiosity-inventory.header, {"context":"category"})", + "title": "t(curiosity-inventory.header, {"context":"quantity"})", "transforms": [ [Function], ], }, { - "title": "t(curiosity-inventory.header, {"context":"Sockets"})", + "title": "t(curiosity-inventory.header, {"context":"subscriptions"})", "transforms": [ [Function], ], }, { - "title": "t(curiosity-inventory.header_last, {"context":"seen"})", + "title": "t(curiosity-inventory.header_next_event, {"context":"date"})", "transforms": [ [Function], ], }, ], "data": { - "cloud_provider": { - "title": "t(curiosity-inventory.header_cloud, {"context":"provider"})", - "value": "dolor sit", + "has_infinite_quantity": { + "title": "t(curiosity-inventory.header_has_infinite, {"context":"quantity"})", + "value": true, }, - "cores": { - "title": "t(curiosity-inventory.header, {"context":"cores"})", - "value": 12, + "next_event_date": { + "title": "t(curiosity-inventory.header_next_event, {"context":"date"})", + "value": "2022-01-01T00:00:00.000Z", }, - "display_name": { - "title": "t(curiosity-inventory.header_display, {"context":"name"})", + "product_name": { + "title": "t(curiosity-inventory.header_product, {"context":"name"})", "value": "lorem", }, - "hardware_type": { - "title": "t(curiosity-inventory.header_hardware, {"context":"type"})", - "value": "ipsum", - }, - "inventory_id": { - "title": "t(curiosity-inventory.header_inventory, {"context":"id"})", - "value": null, - }, - "last_seen": { - "title": "t(curiosity-inventory.header_last, {"context":"seen"})", - "value": null, - }, - "loremIpsum": { - "title": "t(curiosity-inventory.header, {"context":"loremIpsum"})", + "service_level": { + "title": "t(curiosity-inventory.header_service, {"context":"level"})", "value": "hello world", }, - "measurement_type": { - "title": "t(curiosity-inventory.header_measurement, {"context":"type"})", - "value": null, - }, - "number_of_guests": { - "title": "t(curiosity-inventory.header_number_of, {"context":"guests"})", - "value": 3, - }, - "sockets": { - "title": "t(curiosity-inventory.header, {"context":"sockets"})", - "value": 10, + "total_capacity": { + "title": "t(curiosity-inventory.header_total, {"context":"capacity"})", + "value": 2000, }, }, } `; -exports[`Product RHEL config should apply subscriptions inventory configuration: filtered 1`] = ` +exports[`Product RHEL config should apply subscriptions inventory configuration: filtered, fallback display 1`] = ` { "cells": [ { "title": "lorem", }, { - "title": "hello world", + "title": "", }, { "title": "", @@ -460,7 +711,7 @@ exports[`Product RHEL config should apply subscriptions inventory configuration: , }, { - "title": "2022-01-01", + "title": "", }, ], "columnHeaders": [ @@ -500,7 +751,7 @@ exports[`Product RHEL config should apply subscriptions inventory configuration: }, "next_event_date": { "title": "t(curiosity-inventory.header_next_event, {"context":"date"})", - "value": "2022-01-01T00:00:00.000Z", + "value": null, }, "product_name": { "title": "t(curiosity-inventory.header_product, {"context":"name"})", @@ -508,7 +759,7 @@ exports[`Product RHEL config should apply subscriptions inventory configuration: }, "service_level": { "title": "t(curiosity-inventory.header_service, {"context":"level"})", - "value": "hello world", + "value": null, }, "total_capacity": { "title": "t(curiosity-inventory.header_total, {"context":"capacity"})", @@ -518,39 +769,23 @@ exports[`Product RHEL config should apply subscriptions inventory configuration: } `; -exports[`Product RHEL config should apply subscriptions inventory configuration: filtered, fallback display 1`] = ` +exports[`Product RHEL config should apply subscriptions inventory configuration: filtered, infinite 1`] = ` { "cells": [ { "title": "lorem", }, { - "title": "", + "title": "hello world", }, { "title": "", }, { - "title": - - , + "title": 2000, }, { - "title": "", + "title": "2022-01-01", }, ], "columnHeaders": [ @@ -586,11 +821,11 @@ exports[`Product RHEL config should apply subscriptions inventory configuration: "data": { "has_infinite_quantity": { "title": "t(curiosity-inventory.header_has_infinite, {"context":"quantity"})", - "value": true, + "value": false, }, "next_event_date": { "title": "t(curiosity-inventory.header_next_event, {"context":"date"})", - "value": null, + "value": "2022-01-01T00:00:00.000Z", }, "product_name": { "title": "t(curiosity-inventory.header_product, {"context":"name"})", @@ -598,7 +833,7 @@ exports[`Product RHEL config should apply subscriptions inventory configuration: }, "service_level": { "title": "t(curiosity-inventory.header_service, {"context":"level"})", - "value": null, + "value": "hello world", }, "total_capacity": { "title": "t(curiosity-inventory.header_total, {"context":"capacity"})", diff --git a/src/config/__tests__/__snapshots__/product.rhods.test.js.snap b/src/config/__tests__/__snapshots__/product.rhods.test.js.snap index 6be9dc0b4..cc3b83367 100644 --- a/src/config/__tests__/__snapshots__/product.rhods.test.js.snap +++ b/src/config/__tests__/__snapshots__/product.rhods.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Product RHODS config should apply an instances inventory configuration under hosts: filtered 1`] = ` +exports[`Product RHODS config should apply an inventory configuration: filtered 1`] = ` { "cells": [ { @@ -59,7 +59,7 @@ exports[`Product RHODS config should apply an instances inventory configuration } `; -exports[`Product RHODS config should apply an instances inventory configuration under hosts: filtered, authorized 1`] = ` +exports[`Product RHODS config should apply an inventory configuration: filtered, authorized 1`] = ` { "cells": [ { diff --git a/src/config/__tests__/__snapshots__/product.rhosak.test.js.snap b/src/config/__tests__/__snapshots__/product.rhosak.test.js.snap index 16018b348..c71e33d07 100644 --- a/src/config/__tests__/__snapshots__/product.rhosak.test.js.snap +++ b/src/config/__tests__/__snapshots__/product.rhosak.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Product RHOSAK config should apply an instances inventory configuration under hosts: filtered 1`] = ` +exports[`Product RHOSAK config should apply an inventory configuration: filtered 1`] = ` { "cells": [ { @@ -105,7 +105,7 @@ exports[`Product RHOSAK config should apply an instances inventory configuration } `; -exports[`Product RHOSAK config should apply an instances inventory configuration under hosts: filtered, authorized 1`] = ` +exports[`Product RHOSAK config should apply an inventory configuration: filtered, authorized 1`] = ` { "cells": [ { diff --git a/src/config/__tests__/__snapshots__/product.satellite.test.js.snap b/src/config/__tests__/__snapshots__/product.satellite.test.js.snap index 879afc76a..ddbe8ea78 100644 --- a/src/config/__tests__/__snapshots__/product.satellite.test.js.snap +++ b/src/config/__tests__/__snapshots__/product.satellite.test.js.snap @@ -200,12 +200,164 @@ exports[`Product Satellite config should apply guest inventory configuration: fi } `; +exports[`Product Satellite config should apply guest inventory configuration: filtered, missing inventory id 1`] = ` +{ + "cells": [ + { + "title": "lorem", + }, + { + "title": "", + }, + { + "title": , + }, + ], + "columnHeaders": [ + { + "title": "t(curiosity-inventory.header, {"context":"guestsDisplayName"})", + "transforms": [], + }, + { + "title": "t(curiosity-inventory.header, {"context":"inventoryId"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header, {"context":"lastSeen"})", + "transforms": [ + [Function], + ], + }, + ], + "data": { + "displayName": { + "title": "t(curiosity-inventory.header, {"context":"displayName"})", + "value": "lorem", + }, + "inventoryId": { + "title": "t(curiosity-inventory.header, {"context":"inventoryId"})", + "value": "", + }, + "lastSeen": { + "title": "t(curiosity-inventory.header, {"context":"lastSeen"})", + "value": "2022-01-01T00:00:00.000Z", + }, + "loremIpsum": { + "title": "t(curiosity-inventory.header, {"context":"loremIpsum"})", + "value": "hello world", + }, + "subscriptionManagerId": { + "title": "t(curiosity-inventory.header, {"context":"subscriptionManagerId"})", + "value": "lorem subscription id", + }, + }, +} +`; + exports[`Product Satellite config should apply hosts inventory configuration: filtered 1`] = ` { "cells": [ + { + "title": "lorem", + }, { "title": - lorem + t(curiosity-inventory.measurementType, {"context":null}) + + + , + }, + { + "title": 10, + }, + { + "title": , + }, + ], + "columnHeaders": [ + { + "title": "t(curiosity-inventory.header_display_name, {"context":"Satellite"})", + "transforms": [], + }, + { + "title": "t(curiosity-inventory.header_measurement_type, {"context":"Satellite"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_sockets, {"context":"Satellite"})", + "transforms": [ + [Function], + ], + }, + { + "title": "t(curiosity-inventory.header_last_seen, {"context":"Satellite"})", + "transforms": [ + [Function], + ], + }, + ], + "data": { + "cores": { + "title": "t(curiosity-inventory.header_cores, {"context":"Satellite"})", + "value": 12, + }, + "display_name": { + "title": "t(curiosity-inventory.header_display_name, {"context":"Satellite"})", + "value": "lorem", + }, + "hardware_type": { + "title": "t(curiosity-inventory.header_hardware_type, {"context":"Satellite"})", + "value": "ipsum", + }, + "inventory_id": { + "title": "t(curiosity-inventory.header_inventory_id, {"context":"Satellite"})", + "value": "", + }, + "last_seen": { + "title": "t(curiosity-inventory.header_last_seen, {"context":"Satellite"})", + "value": "2022-01-01T00:00:00.000Z", + }, + "loremIpsum": { + "title": "t(curiosity-inventory.header_loremIpsum, {"context":"Satellite"})", + "value": "hello world", + }, + "measurement_type": { + "title": "t(curiosity-inventory.header_measurement_type, {"context":"Satellite"})", + "value": null, + }, + "number_of_guests": { + "title": "t(curiosity-inventory.header_number_of_guests, {"context":"Satellite"})", + "value": 3, + }, + "sockets": { + "title": "t(curiosity-inventory.header_sockets, {"context":"Satellite"})", + "value": 10, + }, + }, +} +`; + +exports[`Product Satellite config should apply hosts inventory configuration: filtered, authorized 1`] = ` +{ + "cells": [ + { + "title": + t(curiosity-inventory.label, {"context":"numberOfGuests","count":3}, [object Object]) , @@ -265,7 +417,7 @@ exports[`Product Satellite config should apply hosts inventory configuration: fi }, "inventory_id": { "title": "t(curiosity-inventory.header_inventory, {"context":"id"})", - "value": "lorem inventory id", + "value": "XXXX-XXXX-XXXXX-XXXXX", }, "last_seen": { "title": "t(curiosity-inventory.header_last, {"context":"seen"})", @@ -317,23 +469,23 @@ exports[`Product Satellite config should apply hosts inventory configuration: fi ], "columnHeaders": [ { - "title": "t(curiosity-inventory.header_display, {"context":"name"})", + "title": "t(curiosity-inventory.header_display_name, {"context":"Satellite"})", "transforms": [], }, { - "title": "t(curiosity-inventory.header_measurement, {"context":"type"})", + "title": "t(curiosity-inventory.header_measurement_type, {"context":"Satellite"})", "transforms": [ [Function], ], }, { - "title": "t(curiosity-inventory.header, {"context":"sockets"})", + "title": "t(curiosity-inventory.header_sockets, {"context":"Satellite"})", "transforms": [ [Function], ], }, { - "title": "t(curiosity-inventory.header_last, {"context":"seen"})", + "title": "t(curiosity-inventory.header_last_seen, {"context":"Satellite"})", "transforms": [ [Function], ], @@ -341,43 +493,43 @@ exports[`Product Satellite config should apply hosts inventory configuration: fi ], "data": { "cloud_provider": { - "title": "t(curiosity-inventory.header_cloud, {"context":"provider"})", + "title": "t(curiosity-inventory.header_cloud_provider, {"context":"Satellite"})", "value": "dolor sit", }, "cores": { - "title": "t(curiosity-inventory.header, {"context":"cores"})", + "title": "t(curiosity-inventory.header_cores, {"context":"Satellite"})", "value": 12, }, "display_name": { - "title": "t(curiosity-inventory.header_display, {"context":"name"})", + "title": "t(curiosity-inventory.header_display_name, {"context":"Satellite"})", "value": "lorem", }, "hardware_type": { - "title": "t(curiosity-inventory.header_hardware, {"context":"type"})", + "title": "t(curiosity-inventory.header_hardware_type, {"context":"Satellite"})", "value": "ipsum", }, "inventory_id": { - "title": "t(curiosity-inventory.header_inventory, {"context":"id"})", + "title": "t(curiosity-inventory.header_inventory_id, {"context":"Satellite"})", "value": null, }, "last_seen": { - "title": "t(curiosity-inventory.header_last, {"context":"seen"})", + "title": "t(curiosity-inventory.header_last_seen, {"context":"Satellite"})", "value": null, }, "loremIpsum": { - "title": "t(curiosity-inventory.header, {"context":"loremIpsum"})", + "title": "t(curiosity-inventory.header_loremIpsum, {"context":"Satellite"})", "value": "hello world", }, "measurement_type": { - "title": "t(curiosity-inventory.header_measurement, {"context":"type"})", + "title": "t(curiosity-inventory.header_measurement_type, {"context":"Satellite"})", "value": null, }, "number_of_guests": { - "title": "t(curiosity-inventory.header_number_of, {"context":"guests"})", + "title": "t(curiosity-inventory.header_number_of_guests, {"context":"Satellite"})", "value": 3, }, "sockets": { - "title": "t(curiosity-inventory.header, {"context":"sockets"})", + "title": "t(curiosity-inventory.header_sockets, {"context":"Satellite"})", "value": 10, }, }, diff --git a/src/config/__tests__/__snapshots__/products.test.js.snap b/src/config/__tests__/__snapshots__/products.test.js.snap new file mode 100644 index 000000000..9ee1cca7c --- /dev/null +++ b/src/config/__tests__/__snapshots__/products.test.js.snap @@ -0,0 +1,137 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Product configurations should return sorted product configs: sorted 1`] = ` +{ + "byGroupIdConfigs": { + "dolor": [ + { + "productGroup": "dolor", + "productId": "dolor sit", + "viewId": "view-dolor", + }, + ], + "hello world": [ + { + "productGroup": "hello world", + "productId": "hello world", + "viewId": "view-hello world", + }, + ], + "lorem": [ + { + "productGroup": "lorem", + "productId": "lorem-ipsum", + "viewId": "view-lorem", + }, + { + "productGroup": "lorem", + "productId": "lorem-dolor", + "viewId": "view-lorem", + }, + ], + }, + "byGroupIds": { + "dolor": [ + "dolor sit", + ], + "hello world": [ + "hello world", + ], + "lorem": [ + "lorem-ipsum", + "lorem-dolor", + ], + }, + "byProductIdConfigs": { + "dolor sit": { + "productGroup": "dolor", + "productId": "dolor sit", + "viewId": "view-dolor", + }, + "hello world": { + "productGroup": "hello world", + "productId": "hello world", + "viewId": "view-hello world", + }, + "lorem-dolor": { + "productGroup": "lorem", + "productId": "lorem-dolor", + "viewId": "view-lorem", + }, + "lorem-ipsum": { + "productGroup": "lorem", + "productId": "lorem-ipsum", + "viewId": "view-lorem", + }, + }, + "byProductIds": [ + "lorem-ipsum", + "lorem-dolor", + "dolor sit", + "hello world", + ], + "byViewIdConfigs": { + "view-dolor": [ + { + "productGroup": "dolor", + "productId": "dolor sit", + "viewId": "view-dolor", + }, + ], + "view-hello world": [ + { + "productGroup": "hello world", + "productId": "hello world", + "viewId": "view-hello world", + }, + ], + "view-lorem": [ + { + "productGroup": "lorem", + "productId": "lorem-ipsum", + "viewId": "view-lorem", + }, + { + "productGroup": "lorem", + "productId": "lorem-dolor", + "viewId": "view-lorem", + }, + ], + }, + "byViewIds": { + "view-dolor": [ + "dolor sit", + ], + "view-hello world": [ + "hello world", + ], + "view-lorem": [ + "lorem-ipsum", + "lorem-dolor", + ], + }, +} +`; + +exports[`Product configurations should return specific methods and properties: products 1`] = ` +{ + "configs": [ + "OpenShift Container Platform", + "OpenShift-dedicated-metrics", + "OpenShift-metrics", + "rhacs", + "RHEL", + "rhods", + "rhosak", + "Satellite", + ], + "sortedConfigs": [ + "byGroupIdConfigs", + "byGroupIds", + "byProductIdConfigs", + "byProductIds", + "byViewIdConfigs", + "byViewIds", + ], +} +`; diff --git a/src/config/__tests__/__snapshots__/routes.test.js.snap b/src/config/__tests__/__snapshots__/routes.test.js.snap new file mode 100644 index 000000000..ef4c60fed --- /dev/null +++ b/src/config/__tests__/__snapshots__/routes.test.js.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Routes configuration should return generated routes: expected paths 1`] = ` +[ + "/openshift-container", + "/OpenShift Container Platform", + "/openshift", + "/OpenShift-metrics", + "/openshift-dedicated-metrics", + "/openshift-dedicated", + "/rhacs", + "/rhel", + "/insights", + "/rhods", + "/rhosak", + "/streams", + "/satellite", + "/optin", + "/", +] +`; diff --git a/src/config/__tests__/index.test.js b/src/config/__tests__/index.test.js index 55f8b9e90..693629801 100644 --- a/src/config/__tests__/index.test.js +++ b/src/config/__tests__/index.test.js @@ -11,7 +11,7 @@ describe('Configuration', () => { const inconsistentEntries = []; - Object.values(config.products).forEach((value, index) => { + Object.values(config.products.configs).forEach((value, index) => { const { productGroup, productId, @@ -37,7 +37,7 @@ describe('Configuration', () => { }; if (Object.values(entryCheck).indexOf('FAIL') > -1) { - inconsistentEntries.push({ [`Entry ${index} inconsistent`]: entryCheck }); + inconsistentEntries.push({ [`Entry ${index}, ${productId} inconsistent`]: entryCheck }); } }); @@ -61,21 +61,19 @@ describe('Configuration', () => { config.routes.forEach((value, index) => { const entryCheck = { - redirect: - typeof config.routes[index].redirect === 'string' || config.routes[index].redirect === null ? 'PASS' : 'FAIL', - isSearchable: typeof config.routes[index].isSearchable === 'boolean' ? 'PASS' : 'FAIL', - aliases: Array.isArray(config.routes[index].aliases) ? 'PASS' : 'FAIL', - activateOnError: typeof config.routes[index].activateOnError === 'boolean' ? 'PASS' : 'FAIL', - disabled: typeof config.routes[index].disabled === 'boolean' ? 'PASS' : 'FAIL', - default: typeof config.routes[index].default === 'boolean' ? 'PASS' : 'FAIL', - component: - typeof config.routes[index].component === 'string' || config.routes[index].component === null - ? 'PASS' - : 'FAIL' + path: typeof value.path === 'string' ? 'PASS' : 'FAIL', + pathParameter: Array.isArray(value.pathParameter) ? 'PASS' : 'FAIL', + productParameter: Array.isArray(value.productParameter) ? 'PASS' : 'FAIL', + productConfig: Array.isArray(value.productConfig) ? 'PASS' : 'FAIL', + redirect: typeof value.redirect === 'string' || value.redirect === null ? 'PASS' : 'FAIL', + activateOnError: typeof value.activateOnError === 'boolean' ? 'PASS' : 'FAIL', + disabled: typeof value.disabled === 'boolean' ? 'PASS' : 'FAIL', + default: typeof value.default === 'boolean' ? 'PASS' : 'FAIL', + component: typeof value.component === 'string' || value.component === null ? 'PASS' : 'FAIL' }; if (Object.values(entryCheck).indexOf('FAIL') > -1) { - inconsistentEntries.push({ [`Entry ${index} inconsistent`]: entryCheck }); + inconsistentEntries.push({ [`Entry ${index}, ${value.path} inconsistent`]: entryCheck }); } }); diff --git a/src/config/__tests__/product.openshiftContainer.test.js b/src/config/__tests__/product.openshiftContainer.test.js index 02118baae..65e76e017 100644 --- a/src/config/__tests__/product.openshiftContainer.test.js +++ b/src/config/__tests__/product.openshiftContainer.test.js @@ -21,7 +21,7 @@ describe('Product OpenShift Container config', () => { const inventoryData = { [INVENTORY_TYPES.DISPLAY_NAME]: 'lorem', - [INVENTORY_TYPES.INVENTORY_ID]: 'lorem inventory id', + [INVENTORY_TYPES.INVENTORY_ID]: undefined, [INVENTORY_TYPES.NUMBER_OF_GUESTS]: 3, [INVENTORY_TYPES.CORES]: 20, [INVENTORY_TYPES.SOCKETS]: 100, @@ -54,6 +54,17 @@ describe('Product OpenShift Container config', () => { expect(fallbackFilteredInventoryData).toMatchSnapshot('filtered, fallback display'); + const filteredInventoryDataAuthorized = parseRowCellsListData({ + filters: initialFilters, + cellData: { + ...inventoryData, + [INVENTORY_TYPES.INVENTORY_ID]: 'XXXX-XXXX-XXXXX-XXXXX' + }, + session: { authorized: { inventory: true } } + }); + + expect(filteredInventoryDataAuthorized).toMatchSnapshot('filtered, authorized'); + expect(inventoryQuery[RHSM_API_QUERY_SET_TYPES.DIRECTION] === SORT_DIRECTION_TYPES.DESCENDING).toBe(true); }); @@ -89,6 +100,16 @@ describe('Product OpenShift Container config', () => { expect(fallbackFilteredInventoryData).toMatchSnapshot('filtered, fallback display'); + const filteredInventoryDataInfinite = parseRowCellsListData({ + filters: initialFilters, + cellData: { + ...inventoryData, + [SUBSCRIPTIONS_INVENTORY_TYPES.HAS_INFINITE_QUANTITY]: false + } + }); + + expect(filteredInventoryDataInfinite).toMatchSnapshot('filtered, infinite'); + expect(inventoryQuery[RHSM_API_QUERY_SET_TYPES.DIRECTION] === SORT_DIRECTION_TYPES.DESCENDING).toBe(true); }); @@ -110,6 +131,16 @@ describe('Product OpenShift Container config', () => { expect(filteredGuestsData).toMatchSnapshot('filtered'); + const filteredGuestsDataMissing = parseRowCellsListData({ + filters: initialFilters, + cellData: { + ...guestsData, + inventoryId: undefined + } + }); + + expect(filteredGuestsDataMissing).toMatchSnapshot('filtered, missing inventory id'); + const filteredGuestsDataAuthorized = parseRowCellsListData({ filters: initialFilters, cellData: guestsData, diff --git a/src/config/__tests__/product.rhacs.test.js b/src/config/__tests__/product.rhacs.test.js index a21f68d73..47d8b21fe 100644 --- a/src/config/__tests__/product.rhacs.test.js +++ b/src/config/__tests__/product.rhacs.test.js @@ -52,7 +52,7 @@ describe('Product RHACS config', () => { /** * FixMe: this test needs to be updated as part of the refactor towards instances vs hosts */ - it('should apply an instances inventory configuration under hosts', () => { + it('should apply an inventory configuration', () => { const { initialInventoryFilters: initialFilters, inventoryHostsQuery: inventoryQuery } = config; const inventoryData = { diff --git a/src/config/__tests__/product.rhel.test.js b/src/config/__tests__/product.rhel.test.js index a9098734d..e6d895083 100644 --- a/src/config/__tests__/product.rhel.test.js +++ b/src/config/__tests__/product.rhel.test.js @@ -16,12 +16,12 @@ describe('Product RHEL config', () => { expect(initialGraphSettings).toMatchSnapshot('settings'); }); - it('should apply hosts inventory configuration', () => { + it('should apply an inventory configuration', () => { const { initialInventoryFilters: initialFilters, inventoryHostsQuery: inventoryQuery, productId } = config; const inventoryData = { [INVENTORY_TYPES.DISPLAY_NAME]: 'lorem', - [INVENTORY_TYPES.INVENTORY_ID]: 'lorem inventory id', + [INVENTORY_TYPES.INVENTORY_ID]: undefined, [INVENTORY_TYPES.HARDWARE_TYPE]: 'ipsum', [INVENTORY_TYPES.MEASUREMENT_TYPE]: null, [INVENTORY_TYPES.NUMBER_OF_GUESTS]: 3, @@ -54,6 +54,17 @@ describe('Product RHEL config', () => { expect(fallbackFilteredInventoryData).toMatchSnapshot('filtered, fallback display'); + const filteredInventoryDataAuthorized = parseRowCellsListData({ + filters: initialFilters, + cellData: { + ...inventoryData, + [INVENTORY_TYPES.INVENTORY_ID]: 'XXXX-XXXX-XXXXX-XXXXX' + }, + session: { authorized: { inventory: true } } + }); + + expect(filteredInventoryDataAuthorized).toMatchSnapshot('filtered, authorized'); + expect(inventoryQuery[RHSM_API_QUERY_SET_TYPES.DIRECTION] === SORT_DIRECTION_TYPES.DESCENDING).toBe(true); }); @@ -89,6 +100,16 @@ describe('Product RHEL config', () => { expect(fallbackFilteredInventoryData).toMatchSnapshot('filtered, fallback display'); + const filteredInventoryDataInfinite = parseRowCellsListData({ + filters: initialFilters, + cellData: { + ...inventoryData, + [SUBSCRIPTIONS_INVENTORY_TYPES.HAS_INFINITE_QUANTITY]: false + } + }); + + expect(filteredInventoryDataInfinite).toMatchSnapshot('filtered, infinite'); + expect(inventoryQuery[RHSM_API_QUERY_SET_TYPES.DIRECTION] === SORT_DIRECTION_TYPES.DESCENDING).toBe(true); }); @@ -110,6 +131,16 @@ describe('Product RHEL config', () => { expect(filteredGuestsData).toMatchSnapshot('filtered'); + const filteredGuestsDataMissing = parseRowCellsListData({ + filters: initialFilters, + cellData: { + ...guestsData, + inventoryId: undefined + } + }); + + expect(filteredGuestsDataMissing).toMatchSnapshot('filtered, missing inventory id'); + const filteredGuestsDataAuthorized = parseRowCellsListData({ filters: initialFilters, cellData: guestsData, diff --git a/src/config/__tests__/product.rhods.test.js b/src/config/__tests__/product.rhods.test.js index 47bf93e06..f19c454fe 100644 --- a/src/config/__tests__/product.rhods.test.js +++ b/src/config/__tests__/product.rhods.test.js @@ -52,7 +52,7 @@ describe('Product RHODS config', () => { /** * FixMe: this test needs to be updated as part of the refactor towards instances vs hosts */ - it('should apply an instances inventory configuration under hosts', () => { + it('should apply an inventory configuration', () => { const { initialInventoryFilters: initialFilters, inventoryHostsQuery: inventoryQuery } = config; const inventoryData = { diff --git a/src/config/__tests__/product.rhosak.test.js b/src/config/__tests__/product.rhosak.test.js index 1c6a79d29..44104e931 100644 --- a/src/config/__tests__/product.rhosak.test.js +++ b/src/config/__tests__/product.rhosak.test.js @@ -52,7 +52,7 @@ describe('Product RHOSAK config', () => { /** * FixMe: this test needs to be updated as part of the refactor towards instances vs hosts */ - it('should apply an instances inventory configuration under hosts', () => { + it('should apply an inventory configuration', () => { const { initialInventoryFilters: initialFilters, inventoryHostsQuery: inventoryQuery } = config; const inventoryData = { diff --git a/src/config/__tests__/product.satellite.test.js b/src/config/__tests__/product.satellite.test.js index 7096ecbad..5dfb5400a 100644 --- a/src/config/__tests__/product.satellite.test.js +++ b/src/config/__tests__/product.satellite.test.js @@ -20,7 +20,7 @@ describe('Product Satellite config', () => { const inventoryData = { [INVENTORY_TYPES.DISPLAY_NAME]: 'lorem', - [INVENTORY_TYPES.INVENTORY_ID]: 'lorem inventory id', + [INVENTORY_TYPES.INVENTORY_ID]: undefined, [INVENTORY_TYPES.HARDWARE_TYPE]: 'ipsum', [INVENTORY_TYPES.MEASUREMENT_TYPE]: null, [INVENTORY_TYPES.NUMBER_OF_GUESTS]: 3, @@ -53,6 +53,17 @@ describe('Product Satellite config', () => { expect(fallbackFilteredInventoryData).toMatchSnapshot('filtered, fallback display'); + const filteredInventoryDataAuthorized = parseRowCellsListData({ + filters: initialFilters, + cellData: { + ...inventoryData, + [INVENTORY_TYPES.INVENTORY_ID]: 'XXXX-XXXX-XXXXX-XXXXX' + }, + session: { authorized: { inventory: true } } + }); + + expect(filteredInventoryDataAuthorized).toMatchSnapshot('filtered, authorized'); + expect(inventoryQuery[RHSM_API_QUERY_SET_TYPES.DIRECTION] === SORT_DIRECTION_TYPES.DESCENDING).toBe(true); }); @@ -74,6 +85,16 @@ describe('Product Satellite config', () => { expect(filteredGuestsData).toMatchSnapshot('filtered'); + const filteredGuestsDataMissing = parseRowCellsListData({ + filters: initialFilters, + cellData: { + ...guestsData, + inventoryId: undefined + } + }); + + expect(filteredGuestsDataMissing).toMatchSnapshot('filtered, missing inventory id'); + const filteredGuestsDataAuthorized = parseRowCellsListData({ filters: initialFilters, cellData: guestsData, diff --git a/src/config/__tests__/products.test.js b/src/config/__tests__/products.test.js new file mode 100644 index 000000000..dcfad1455 --- /dev/null +++ b/src/config/__tests__/products.test.js @@ -0,0 +1,37 @@ +import { products } from '../products'; + +describe('Product configurations', () => { + it('should return specific methods and properties', () => { + expect({ + configs: products.configs.map(({ productId }) => productId), + sortedConfigs: Object.keys(products.sortedConfigs()) + }).toMatchSnapshot('products'); + }); + + it('should return sorted product configs', () => { + expect( + products.sortedConfigs([ + { + productGroup: 'lorem', + productId: 'lorem-ipsum', + viewId: `view-lorem` + }, + { + productGroup: 'lorem', + productId: 'lorem-dolor', + viewId: `view-lorem` + }, + { + productGroup: 'dolor', + productId: 'dolor sit', + viewId: `view-dolor` + }, + { + productGroup: 'hello world', + productId: 'hello world', + viewId: `view-hello world` + } + ]) + ).toMatchSnapshot('sorted'); + }); +}); diff --git a/src/config/__tests__/routes.test.js b/src/config/__tests__/routes.test.js new file mode 100644 index 000000000..c3aae2f72 --- /dev/null +++ b/src/config/__tests__/routes.test.js @@ -0,0 +1,7 @@ +import { routes } from '../routes'; + +describe('Routes configuration', () => { + it('should return generated routes', () => { + expect(routes.map(({ path }) => path)).toMatchSnapshot('expected paths'); + }); +}); diff --git a/src/config/index.js b/src/config/index.js index 3b2369cd5..a9b0954d3 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,25 +1,11 @@ -import { config as rhel } from './product.rhel'; -import { config as openshiftContainer } from './product.openshiftContainer'; -import { config as openshiftMetrics } from './product.openshiftMetrics'; -import { config as openshiftDedicated } from './product.openshiftDedicated'; -import { config as rhosak } from './product.rhosak'; -import { config as satelliteProduct } from './product.satellite'; import rbacConfig from './rbac.json'; +import { products as productConfig } from './products'; import { routes as routesConfig } from './routes'; -const productsConfig = { - rhel, - rhosak, - openshiftContainer, - openshiftMetrics, - openshiftDedicated, - satelliteProduct -}; - const config = { - products: productsConfig, + products: productConfig, rbac: rbacConfig, routes: routesConfig }; -export { config as default, config, productsConfig, rbacConfig, routesConfig }; +export { config as default, config, productConfig, rbacConfig, routesConfig }; diff --git a/src/config/product.openshiftContainer.js b/src/config/product.openshiftContainer.js index a6c002d3a..96dd8a9a3 100644 --- a/src/config/product.openshiftContainer.js +++ b/src/config/product.openshiftContainer.js @@ -28,16 +28,18 @@ import { translate } from '../components/i18n/i18n'; // ToDo: evaluate the need for "productLabel" or using productId -const productGroup = RHSM_API_PATH_PRODUCT_TYPES.OPENSHIFT; +const productGroup = 'openshift-container'; const productId = RHSM_API_PATH_PRODUCT_TYPES.OPENSHIFT; const productLabel = RHSM_API_PATH_PRODUCT_TYPES.OPENSHIFT; const config = { + aliases: [RHSM_API_PATH_PRODUCT_TYPES.OPENSHIFT, 'openshift'], productGroup, productId, productLabel, + productPath: productGroup.toLowerCase(), productDisplay: DISPLAY_TYPES.PARTIAL, viewId: `view${productGroup}`, productContextFilterUom: true, diff --git a/src/config/product.openshiftDedicated.js b/src/config/product.openshiftDedicated.js index 2552ad89c..2d1ffac03 100644 --- a/src/config/product.openshiftDedicated.js +++ b/src/config/product.openshiftDedicated.js @@ -29,9 +29,11 @@ const productId = RHSM_API_PATH_PRODUCT_TYPES.OPENSHIFT_DEDICATED_METRICS; const productLabel = RHSM_API_PATH_PRODUCT_TYPES.OPENSHIFT_DEDICATED_METRICS; const config = { + aliases: ['openshift-dedicated'], productGroup, productId, productLabel, + productPath: productGroup.toLowerCase(), productDisplay: DISPLAY_TYPES.DUAL_AXES, viewId: `view${productGroup}`, query: { diff --git a/src/config/product.openshiftMetrics.js b/src/config/product.openshiftMetrics.js index df9f04b9c..48b9b50f7 100644 --- a/src/config/product.openshiftMetrics.js +++ b/src/config/product.openshiftMetrics.js @@ -21,18 +21,20 @@ import { translate } from '../components/i18n/i18n'; // ToDo: evaluate the need for "productLabel" or using productId -const productGroup = RHSM_API_PATH_PRODUCT_TYPES.OPENSHIFT_METRICS; +const productGroup = 'openshift-container'; const productId = RHSM_API_PATH_PRODUCT_TYPES.OPENSHIFT_METRICS; const productLabel = RHSM_API_PATH_PRODUCT_TYPES.OPENSHIFT_METRICS; const config = { + aliases: [RHSM_API_PATH_PRODUCT_TYPES.OPENSHIFT_METRICS, 'openshift'], productGroup, productId, productLabel, + productPath: productGroup.toLowerCase(), productDisplay: DISPLAY_TYPES.PARTIAL, - viewId: `view${productGroup}`, + viewId: `view${productGroup}-${productId}`, query: { [RHSM_API_QUERY_SET_TYPES.START_DATE]: dateHelpers.getRangedMonthDateTime('current').value.startDate.toISOString(), [RHSM_API_QUERY_SET_TYPES.END_DATE]: dateHelpers.getRangedMonthDateTime('current').value.endDate.toISOString() diff --git a/src/config/product.rhacs.js b/src/config/product.rhacs.js index 1254f3a9b..46a77f6d8 100644 --- a/src/config/product.rhacs.js +++ b/src/config/product.rhacs.js @@ -36,9 +36,11 @@ const productId = RHSM_API_PATH_PRODUCT_TYPES.RHACS; const productLabel = RHSM_API_PATH_PRODUCT_TYPES.RHACS; const config = { + aliases: [], productGroup, productId, productLabel, + productPath: productGroup.toLowerCase(), productDisplay: DISPLAY_TYPES.HOURLY, viewId: `view${productGroup}`, query: { diff --git a/src/config/product.rhel.js b/src/config/product.rhel.js index 9fb2aefeb..04b180e2b 100644 --- a/src/config/product.rhel.js +++ b/src/config/product.rhel.js @@ -41,14 +41,24 @@ import { translate } from '../components/i18n/i18n'; const productGroup = RHSM_API_PATH_PRODUCT_TYPES.RHEL; -const productId = null; +const productId = RHSM_API_PATH_PRODUCT_TYPES.RHEL; const productLabel = RHSM_API_PATH_PRODUCT_TYPES.RHEL; +/** + * RHEL product config + * + * @type {{aliases: string[], productGroup: string, productId: string, productLabel: string, productDisplay: string, viewId: string, + * productArchitectures: string[], query: object, graphTallyQuery: object, inventoryHostQuery: object, inventorySubscriptionsQuery: object, + * initialGraphFilters: {}[], initialGraphSettings: object, initialGuestsFilters: {}[], initialInventoryFilters: {}[], + * initialSubscriptionsInventoryFilters: {}[], initialToolbarFilters: {}[], }} + */ const config = { + aliases: ['insights'], productGroup, productId, productLabel, + productPath: productGroup.toLowerCase(), productDisplay: DISPLAY_TYPES.CAPACITY, viewId: `view${productGroup}`, productArchitectures: [...Object.values(RHSM_API_PATH_PRODUCT_ARCHITECTURE_RHEL_TYPES)], diff --git a/src/config/product.rhods.js b/src/config/product.rhods.js index 43d6f4c43..8248faeba 100644 --- a/src/config/product.rhods.js +++ b/src/config/product.rhods.js @@ -31,9 +31,11 @@ const productId = RHSM_API_PATH_PRODUCT_TYPES.RHODS; const productLabel = RHSM_API_PATH_PRODUCT_TYPES.RHODS; const config = { + aliases: [], productGroup, productId, productLabel, + productPath: productGroup.toLowerCase(), productDisplay: DISPLAY_TYPES.HOURLY, viewId: `view${productGroup}`, query: { diff --git a/src/config/product.rhosak.js b/src/config/product.rhosak.js index 2117d75e0..a7f97d9b7 100644 --- a/src/config/product.rhosak.js +++ b/src/config/product.rhosak.js @@ -40,9 +40,11 @@ const productId = RHSM_API_PATH_PRODUCT_TYPES.RHOSAK; const productLabel = RHSM_API_PATH_PRODUCT_TYPES.RHOSAK; const config = { + aliases: ['streams'], productGroup, productId, productLabel, + productPath: productGroup.toLowerCase(), productDisplay: DISPLAY_TYPES.HOURLY, viewId: `view${productGroup}`, query: { diff --git a/src/config/product.satellite.js b/src/config/product.satellite.js index 195932242..771d6f89d 100644 --- a/src/config/product.satellite.js +++ b/src/config/product.satellite.js @@ -33,14 +33,24 @@ import { translate } from '../components/i18n/i18n'; const productGroup = RHSM_API_PATH_PRODUCT_TYPES.SATELLITE; -const productId = null; +const productId = RHSM_API_PATH_PRODUCT_TYPES.SATELLITE; const productLabel = RHSM_API_PATH_PRODUCT_TYPES.SATELLITE; +/** + * Satellite product config + * + * @type {{aliases: string[], productGroup: string, productId: string, productLabel: string, productDisplay: string, viewId: string, + * productVariants: string[], query: object, graphTallyQuery: object, inventoryHostQuery: object, + * inventorySubscriptionsQuery: object, initialGraphFilters: {}[], initialGraphSettings: object, initialGuestsFilters: {}[], + * initialInventoryFilters: {}[], initialToolbarFilters: {}[], }[]} + */ const config = { + aliases: [], productGroup, productId, productLabel, + productPath: productGroup.toLowerCase(), productDisplay: DISPLAY_TYPES.PARTIAL, viewId: `view${productGroup}`, productVariants: [...Object.values(RHSM_API_PATH_PRODUCT_VARIANT_SATELLITE_TYPES)], diff --git a/src/config/products.js b/src/config/products.js new file mode 100644 index 000000000..6b6215e99 --- /dev/null +++ b/src/config/products.js @@ -0,0 +1,89 @@ +import _memoize from 'lodash/memoize'; + +/** + * IIFE for generating a product configs listing via webpack + * + * @type {{aliases: string[], productGroup: string, productId: string, productLabel: string, productDisplay: string, viewId: string, + * productArchitectures: string[], productVariants: string[], query: object, graphTallyQuery: object, inventoryHostQuery: object, + * inventorySubscriptionsQuery: object, initialGraphFilters: {}[], initialGraphSettings: object, initialGuestsFilters: {}[], + * initialInventoryFilters: {}[], initialSubscriptionsInventoryFilters: {}[], initialToolbarFilters: {}[], }[]} + */ +const productConfigs = (() => { + try { + const path = require.context('./', false, /product\.[\d\D]+\.js$/i); + return path.keys().map(path); + } catch (e) { + /** + * Basic configuration for testing only. + */ + if (process.env.REACT_APP_ENV === 'test' && require) { + return [ + ...require('fs') // eslint-disable-line + ?.readdirSync('./src/config') // eslint-disable-line + ?.filter(file => /product\.[a-z]+\.js/i.test(file)) // eslint-disable-line + ?.map(file => require(`./${file}`)) // eslint-disable-line + ]; + } + + console.warn(`Product configuration failed to load: ${e.message}`); + return []; + } +})()?.map(value => value.config); + +/** + * Sorted/organized/grouped product configs. + * - byGroupIdConfigs, object of productGroup properties against an array of associated product configs + * - byViewIds, object of viewId properties against an array of associated productId strings. "viewId" was created because of the + * overlap with productIds and productGroups, this may be refactored in the future + * - byProductIds, a unique array of all productId strings + * - byGroupIds, object of productGroup properties against an array of associated productId strings. + * - byViewIdConfigs, object of viewId properties against an array of associated product configs + * - byProductIdConfigs, object of productId properties against a product config + * + * @param {productConfigs} configs + * @returns {{byGroupIdConfigs: {}, byViewIds: {}, byProductIds: any[], byGroupIds: {}, byViewIdConfigs: {}, byProductIdConfigs: {}}} + */ +const sortedProductConfigs = _memoize((configs = productConfigs) => { + const productIds = new Set(); + const productIdConfigs = {}; + const groupIdConfigs = {}; + const groupedGroupIds = {}; + const viewIdConfigs = {}; + const groupedViewIds = {}; + + configs?.forEach(config => { + Object.freeze(config); + + const { productGroup, productId, viewId } = config; + productIdConfigs[productId] = config; + productIds.add(productId); + + groupIdConfigs[productGroup] ??= []; + groupIdConfigs[productGroup].push(config); + + groupedGroupIds[productGroup] ??= []; + groupedGroupIds[productGroup].push(productId); + + viewIdConfigs[viewId] ??= []; + viewIdConfigs[viewId].push(config); + + groupedViewIds[viewId] ??= []; + groupedViewIds[viewId].push(productId); + }); + + return { + byGroupIdConfigs: groupIdConfigs, + byGroupIds: groupedGroupIds, + byProductIdConfigs: productIdConfigs, + byProductIds: Array.from(productIds), + byViewIdConfigs: viewIdConfigs, + byViewIds: groupedViewIds + }; +}); + +const products = { + configs: productConfigs, + sortedConfigs: sortedProductConfigs +}; + +export { products as default, products, productConfigs, sortedProductConfigs }; diff --git a/src/config/routes.js b/src/config/routes.js index d88b26901..ebd3517eb 100644 --- a/src/config/routes.js +++ b/src/config/routes.js @@ -1,122 +1,31 @@ -import { config as rhelConfig } from './product.rhel'; -import { config as openshiftContainerConfig } from './product.openshiftContainer'; -import { config as openshiftMetricsConfig } from './product.openshiftMetrics'; -import { config as openshiftDedicatedConfig } from './product.openshiftDedicated'; -import { config as rhacsConfig } from './product.rhacs'; -import { config as rhodsConfig } from './product.rhods'; -import { config as rhosakConfig } from './product.rhosak'; -import { config as satelliteProductConfig } from './product.satellite'; -import { RHSM_API_PATH_PRODUCT_TYPES } from '../services/rhsm/rhsmConstants'; +import { products } from './products'; import { helpers } from '../common'; const routes = [ - { - id: 'rhel', - path: '/rhel', - pathParameter: [RHSM_API_PATH_PRODUCT_TYPES.RHEL], - productParameter: [rhelConfig.productGroup], - productConfig: [{ ...rhelConfig, productId: RHSM_API_PATH_PRODUCT_TYPES.RHEL }], - redirect: null, - isSearchable: true, - aliases: ['insights'], - activateOnError: false, - disabled: helpers.UI_DISABLED, - default: false, - component: 'productView/productView' - }, - { - id: 'openshift-container', - path: '/openshift-container', - pathParameter: [openshiftContainerConfig.productId, openshiftMetricsConfig.productId], - productParameter: [openshiftContainerConfig.productGroup, openshiftMetricsConfig.productGroup], - productConfig: [openshiftContainerConfig, openshiftMetricsConfig], - redirect: null, - isSearchable: true, - aliases: [], - activateOnError: false, - disabled: helpers.UI_DISABLED, - default: false, - component: 'productView/productView' - }, - { - id: 'openshift-dedicated', - path: '/openshift-dedicated', - pathParameter: [openshiftDedicatedConfig.productId], - productParameter: [openshiftDedicatedConfig.productGroup], - productConfig: [openshiftDedicatedConfig], - redirect: null, - isSearchable: true, - aliases: [], - activateOnError: false, - disabled: helpers.UI_DISABLED, - default: false, - component: 'productView/productView' - }, - { - id: 'rhacs', - path: '/rhacs', - pathParameter: [rhacsConfig.productId], - productParameter: [rhacsConfig.productGroup], - productConfig: [rhacsConfig], - redirect: null, - isSearchable: true, - aliases: ['rhacs'], - activateOnError: false, - disabled: helpers.UI_DISABLED, - default: false, - component: 'productView/productView' - }, - { - id: 'rhods', - path: '/rhods', - pathParameter: [rhodsConfig.productId], - productParameter: [rhodsConfig.productGroup], - productConfig: [rhodsConfig], - redirect: null, - isSearchable: true, - aliases: ['rhods'], - activateOnError: false, - disabled: helpers.UI_DISABLED, - default: false, - component: 'productView/productView' - }, - { - id: 'rhosak', - path: '/streams', - pathParameter: [rhosakConfig.productId], - productParameter: [rhosakConfig.productGroup], - productConfig: [rhosakConfig], - redirect: null, - isSearchable: true, - aliases: ['application-services', 'streams', 'rhosak'], - activateOnError: false, - disabled: helpers.UI_DISABLED, - default: false, - component: 'productView/productView' - }, - { - id: 'satellite', - path: '/satellite', - pathParameter: [RHSM_API_PATH_PRODUCT_TYPES.SATELLITE], - productParameter: [satelliteProductConfig.productGroup], - productConfig: [{ ...satelliteProductConfig, productId: RHSM_API_PATH_PRODUCT_TYPES.SATELLITE }], - redirect: null, - isSearchable: true, - aliases: [], - activateOnError: false, - disabled: helpers.UI_DISABLED, - default: false, - component: 'productView/productView' - }, + ...Object.entries(products.sortedConfigs().byGroupIdConfigs).reduce((acc, [, groupedConfigs]) => { + const updatedConfig = [ + { + path: `/${groupedConfigs?.[0]?.productPath}`, + pathParameter: [...groupedConfigs.map(({ productId }) => productId)], + productParameter: [...groupedConfigs.map(({ productId }) => productId)], + productConfig: [...groupedConfigs], + redirect: null, + activateOnError: false, + disabled: helpers.UI_DISABLED, + default: false, + component: 'productView/productView' + } + ]; + + const flatAliases = groupedConfigs.map(({ aliases }) => [...aliases]).flat(1); + const aliasedConfigs = [...[...new Set(flatAliases)].map(alias => ({ ...updatedConfig[0], path: `/${alias}` }))]; + + return [...acc, ...updatedConfig, ...aliasedConfigs]; + }, []), { id: 'optin', path: '/optin', - pathParameter: null, - productParameter: null, - productConfig: null, redirect: null, - isSearchable: false, - aliases: [], activateOnError: true, disabled: helpers.UI_DISABLED, default: false, @@ -125,12 +34,7 @@ const routes = [ { id: 'missing', path: '/', - pathParameter: null, - productParameter: null, - productConfig: null, - redirect: '/', - isSearchable: false, - aliases: [], + redirect: './', activateOnError: false, disabled: helpers.UI_DISABLED, default: true, diff --git a/src/hooks/__tests__/__snapshots__/useRouter.test.js.snap b/src/hooks/__tests__/__snapshots__/useRouter.test.js.snap index 5c52722b9..c4968dad7 100644 --- a/src/hooks/__tests__/__snapshots__/useRouter.test.js.snap +++ b/src/hooks/__tests__/__snapshots__/useRouter.test.js.snap @@ -1,36 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`useRouter should apply a hook for useHistory that emulates history but allows dispatching platform navigation updates if available: dispatch platform navigation 1`] = ` -[ - [ - { - "meta": { - "appName": undefined, - "id": "rhel", - "secondaryNav": undefined, - }, - "payload": Promise {}, - "type": "PLATFORM_SET_NAV", - }, - ], -] -`; - -exports[`useRouter should apply a hook for useHistory that emulates history but allows dispatching platform navigation updates if available: history push 1`] = ` +exports[`useRouter should apply a hook for useHistory: history push 1`] = ` [ [ - "/lorem/ipsum", + "/dolor/sit", undefined, ], -] -`; - -exports[`useRouter should apply a hook for useHistory: dispatch platform navigation 1`] = `[]`; - -exports[`useRouter should apply a hook for useHistory: history push 1`] = ` -[ [ - "/lorem/ipsum", + "/rhel", undefined, ], [ diff --git a/src/hooks/__tests__/useRouter.test.js b/src/hooks/__tests__/useRouter.test.js index 0f97043be..dc3ad3db6 100644 --- a/src/hooks/__tests__/useRouter.test.js +++ b/src/hooks/__tests__/useRouter.test.js @@ -6,37 +6,17 @@ describe('useRouter', () => { }); it('should apply a hook for useHistory', () => { - const mockDispatch = jest.fn(); const mockHistoryPush = jest.fn(); const { result: mockUseHistory } = shallowHook(() => useHistory({ - useDispatch: () => action => action(mockDispatch), useHistory: () => ({ push: mockHistoryPush }) }) ); - mockUseHistory.push('/lorem/ipsum'); + mockUseHistory.push('/dolor/sit'); mockUseHistory.push('rhel'); + mockUseHistory.push('insights'); - expect(mockDispatch.mock.calls).toMatchSnapshot('dispatch platform navigation'); - expect(mockHistoryPush.mock.calls).toMatchSnapshot('history push'); - }); - - it('should apply a hook for useHistory that emulates history but allows dispatching platform navigation updates if available', () => { - const mockDispatch = jest.fn(); - const mockHistoryPush = jest.fn(); - const { result: mockUseHistory } = shallowHook(() => - useHistory({ - isSetAppNav: true, - useDispatch: () => action => action(mockDispatch), - useHistory: () => ({ push: mockHistoryPush }) - }) - ); - - mockUseHistory.push('/lorem/ipsum'); - mockUseHistory.push('rhel'); - - expect(mockDispatch.mock.calls).toMatchSnapshot('dispatch platform navigation'); expect(mockHistoryPush.mock.calls).toMatchSnapshot('history push'); }); }); diff --git a/src/hooks/useRouter.js b/src/hooks/useRouter.js index d2c024439..343fdba59 100644 --- a/src/hooks/useRouter.js +++ b/src/hooks/useRouter.js @@ -1,37 +1,30 @@ import { useHistory as useHistoryRRD, useLocation, useParams, useRouteMatch } from 'react-router-dom'; import { useRouteDetail } from '../components/router/routerContext'; import { routerHelpers } from '../components/router/routerHelpers'; -import { reduxActions, storeHooks } from '../redux'; /** * Pass useHistory methods. Proxy useHistory push with Platform specific navigation update. * * @param {object} options - * @param {boolean} options.isSetAppNav Allow setting the Platform's left navigation if conditions are met or fallback to history.push. * @param {Function} options.useHistory - * @param {Function} options.useDispatch * @returns {object} */ -const useHistory = ({ - isSetAppNav = false, - useHistory: useAliasHistory = useHistoryRRD, - useDispatch: useAliasDispatch = storeHooks.reactRedux.useDispatch -} = {}) => { +const useHistory = ({ useHistory: useAliasHistory = useHistoryRRD } = {}) => { const history = useAliasHistory(); - const dispatch = useAliasDispatch(); return { ...history, push: (pathLocation, historyState) => { const pathName = (typeof pathLocation === 'string' && pathLocation) || pathLocation?.pathname; - const { productParameter, id, routeHref } = routerHelpers.getRouteConfig({ pathName, id: pathName }); + const { firstMatch } = routerHelpers.getRouteConfigByPath({ pathName }); const { hash, search } = window.location; - if (isSetAppNav && productParameter) { - return dispatch(reduxActions.platform.setAppNav(id)); - } - - return history?.push(routeHref || (pathName && `${pathName}${search}${hash}`) || pathLocation, historyState); + return history?.push( + (firstMatch?.productPath && `${routerHelpers.pathJoin('/', firstMatch?.productPath)}${search}${hash}`) || + (pathName && `${pathName}${search}${hash}`) || + pathLocation, + historyState + ); } }; }; diff --git a/src/index.js b/src/index.js index 7f0975627..495dd16ea 100644 --- a/src/index.js +++ b/src/index.js @@ -10,10 +10,7 @@ window.insights = { chrome: { appNavClick: ({ id, ...rest }) => { console.log(`Emulated appNavClick: ${JSON.stringify({ id, ...rest })}`); - document.location.href = routerHelpers.pathJoin( - document.location.pathname, - routerHelpers.getRouteConfig({ id }).path - ); + document.location.href = routerHelpers.pathJoin(routerHelpers.dynamicBaseName(), id); }, auth: { getUser: () => diff --git a/src/redux/reducers/viewReducer.js b/src/redux/reducers/viewReducer.js index 98d48f1ce..b293906c2 100644 --- a/src/redux/reducers/viewReducer.js +++ b/src/redux/reducers/viewReducer.js @@ -1,4 +1,4 @@ -import { routerHelpers } from '../../components/router'; +import { productConfig } from '../../config'; import { reduxTypes } from '../types'; import { reduxHelpers } from '../common/reduxHelpers'; import { RHSM_API_QUERY_SET_TYPES as RHSM_API_QUERY_TYPES } from '../../services/rhsm/rhsmConstants'; @@ -29,7 +29,7 @@ const viewReducer = (state = initialState, action) => { switch (action.type) { case reduxTypes.query.SET_QUERY_RESET_INVENTORY_LIST: const updateResetQueries = (query = {}, id) => { - const queryIds = routerHelpers.productGroups[id] || (query[id] && [id]) || []; + const queryIds = productConfig.sortedConfigs().byViewIds[id] || (query[id] && [id]) || []; const updatedQuery = { ...query }; queryIds.forEach(queryId => { @@ -60,7 +60,7 @@ const viewReducer = (state = initialState, action) => { ); case reduxTypes.query.SET_QUERY_CLEAR_INVENTORY_LIST: const updateClearQueries = (query = {}, id) => { - const queryIds = routerHelpers.productGroups[id] || (query[id] && [id]) || []; + const queryIds = productConfig.sortedConfigs().byViewIds[id] || (query[id] && [id]) || []; const updatedQuery = { ...query }; queryIds.forEach(queryId => { @@ -88,7 +88,7 @@ const viewReducer = (state = initialState, action) => { ); case reduxTypes.query.SET_QUERY_CLEAR_INVENTORY_GUESTS_LIST: const updateClearGuestQuery = (query = {}, id) => { - const queryIds = routerHelpers.productGroups[id] || (query[id] && [id]) || []; + const queryIds = productConfig.sortedConfigs().byViewIds[id] || (query[id] && [id]) || []; const updatedQuery = { ...query }; queryIds.forEach(queryId => { diff --git a/tests/__snapshots__/code.test.js.snap b/tests/__snapshots__/code.test.js.snap index 3d6aacc30..3d1b2795a 100644 --- a/tests/__snapshots__/code.test.js.snap +++ b/tests/__snapshots__/code.test.js.snap @@ -6,6 +6,7 @@ exports[`General code checks should only have specific console.[warn|log|info|er "components/inventoryCard/inventoryCardContext.js:229: console.warn(\`Sorting can only be performed on select fields, confirm field \${id} is allowed.\`);", "components/inventoryCard/inventoryCardHelpers.js:87: console.warn(\`Warning: Filter "\${id}" not found in "table row" response data.\`, cellData);", "components/inventoryCardSubscriptions/inventoryCardSubscriptionsContext.js:126: console.warn(\`Sorting can only be performed on select fields, confirm field \${id} is allowed.\`);", + "config/products.js:28: console.warn(\`Product configuration failed to load: \${e.message}\`);", "index.js:12: console.log(\`Emulated appNavClick: \${JSON.stringify({ id, ...rest })}\`);", "redux/common/reduxHelpers.js:282: console.error(\`Error: Property \${prop} does not exist within the passed state.\`, state);", "redux/common/reduxHelpers.js:286: console.warn(\`Warning: Property \${prop} does not exist within the passed initialState.\`, initialState);", diff --git a/tests/__snapshots__/dist.test.js.snap b/tests/__snapshots__/dist.test.js.snap index d1fee1640..aebda190d 100644 --- a/tests/__snapshots__/dist.test.js.snap +++ b/tests/__snapshots__/dist.test.js.snap @@ -4,19 +4,43 @@ exports[`Build distribution should have a predictable ephemeral navigation based [ { "coverage": "TRUE", - "path": "/rhel", + "path": "/openshift-container", "productParameter": [ - "RHEL", + "OpenShift Container Platform", + "OpenShift-metrics", ], }, { - "coverage": "TRUE", - "path": "/openshift-container", + "coverage": "FALSE", + "path": "/OpenShift Container Platform", + "productParameter": [ + "OpenShift Container Platform", + "OpenShift-metrics", + ], + }, + { + "coverage": "FALSE", + "path": "/openshift", + "productParameter": [ + "OpenShift Container Platform", + "OpenShift-metrics", + ], + }, + { + "coverage": "FALSE", + "path": "/OpenShift-metrics", "productParameter": [ "OpenShift Container Platform", "OpenShift-metrics", ], }, + { + "coverage": "FALSE", + "path": "/openshift-dedicated-metrics", + "productParameter": [ + "OpenShift-dedicated-metrics", + ], + }, { "coverage": "TRUE", "path": "/openshift-dedicated", @@ -31,6 +55,20 @@ exports[`Build distribution should have a predictable ephemeral navigation based "rhacs", ], }, + { + "coverage": "TRUE", + "path": "/rhel", + "productParameter": [ + "RHEL", + ], + }, + { + "coverage": "FALSE", + "path": "/insights", + "productParameter": [ + "RHEL", + ], + }, { "coverage": "TRUE", "path": "/rhods", @@ -38,6 +76,13 @@ exports[`Build distribution should have a predictable ephemeral navigation based "rhods", ], }, + { + "coverage": "FALSE", + "path": "/rhosak", + "productParameter": [ + "rhosak", + ], + }, { "coverage": "TRUE", "path": "/streams", @@ -55,12 +100,12 @@ exports[`Build distribution should have a predictable ephemeral navigation based { "coverage": "FALSE", "path": "/optin", - "productParameter": null, + "productParameter": undefined, }, { "coverage": "FALSE", "path": "/", - "productParameter": null, + "productParameter": undefined, }, ] `;