From b846b19d5771920aa931b8dc9ad7bf74faff949e Mon Sep 17 00:00:00 2001 From: Katia Aresti Date: Mon, 6 Nov 2023 10:50:47 +0100 Subject: [PATCH] ISPN-15284 Refactor Select: SecuredCacheConfigurator --- cypress/e2e/1_acess_management.cy.js | 1 + cypress/e2e/3_cache-crud-wizard.cy.js | 37 ++++++----- src/app/CacheManagers/CacheTableDisplay.tsx | 17 ++--- .../Features/SecuredCacheConfigurator.tsx | 65 +++++++++++-------- src/app/assets/languages/en.json | 1 + src/app/services/rolesHook.ts | 26 ++++++++ 6 files changed, 95 insertions(+), 52 deletions(-) diff --git a/cypress/e2e/1_acess_management.cy.js b/cypress/e2e/1_acess_management.cy.js index d85688493..4c17aaf1b 100644 --- a/cypress/e2e/1_acess_management.cy.js +++ b/cypress/e2e/1_acess_management.cy.js @@ -29,6 +29,7 @@ describe('Global stats', () => { cy.get("[aria-label=role-description-input]").type("aRole description"); cy.get("[data-cy=dropdown-button-permissions").click(); cy.get("#select-multi-typeahead-ALL").click(); + cy.get("[data-cy=dropdown-button-permissions").click(); cy.get("[aria-label=Create]").click(); cy.contains('Role aRole has been created'); cy.contains('aRole description'); diff --git a/cypress/e2e/3_cache-crud-wizard.cy.js b/cypress/e2e/3_cache-crud-wizard.cy.js index 55adfb23a..6aabd04bc 100644 --- a/cypress/e2e/3_cache-crud-wizard.cy.js +++ b/cypress/e2e/3_cache-crud-wizard.cy.js @@ -14,8 +14,10 @@ describe('Cache Creation Wizard', () => { cy.get('[data-cy=wizardNextButton]').click(); //Filling bounded cache properties - cy.get('#featuresSelect-select-multi-typeahead-typeahead').click().type('boun'); - cy.get('#BOUNDED > button').click(); + cy.get('[data-cy=featuresSelect]').click(); + cy.get('#select-multi-typeahead-Bounded').click(); + cy.get('[data-cy=featuresSelect]').click(); + cy.get('#size').click(); cy.get('[data-cy=memorySizeInput]').clear().type('1'); cy.get('#memorySizeUnit').click(); @@ -24,8 +26,9 @@ describe('Cache Creation Wizard', () => { cy.get('#REMOVE').click(); //Filling indexed cache properties - cy.get('#featuresSelect-select-multi-typeahead-typeahead').click().type('ind'); - cy.get('#INDEXED > button').click(); + cy.get('[data-cy=featuresSelect]').click(); + cy.get('#select-multi-typeahead-Indexed').click(); + cy.get('[data-cy=featuresSelect]').click(); cy.get('#persistent').click(); cy.get('#auto').click(); cy.get('#volatile').click(); @@ -37,18 +40,18 @@ describe('Cache Creation Wizard', () => { cy.get('[id$="org.infinispan.Car"]').click(); //Filling auth cache properties - cy.get('#featuresSelect-select-multi-typeahead-typeahead').click().type('auth'); - cy.get('#SECURED > button').click(); - cy.get('#roleSelector-select-multi-typeahead-typeahead').click().type('admin'); - cy.get('#admin > button').click(); - cy.get('#roleSelector-select-multi-typeahead-typeahead').click().type('application'); - cy.get('#application > button').click(); - cy.get('#roleSelector-select-multi-typeahead-typeahead').click().type('deployer'); - cy.get('#deployer > button').click(); + cy.get('[data-cy=featuresSelect]').click(); + cy.get('#select-multi-typeahead-Authorization').click(); + cy.get('[data-cy=roleSelector]').click(); + cy.get("#select-multi-typeahead-admin").click(); + cy.get("#select-multi-typeahead-application").click(); + cy.get("#select-multi-typeahead-deployer").click(); + cy.get('[data-cy=roleSelector]').click(); //Filling persistant cache properties - cy.get('#featuresSelect-select-multi-typeahead-typeahead').click().type('pers'); - cy.get('#PERSISTENCE > button').click(); + cy.get('[data-cy=featuresSelect]').click(); + cy.get('#select-multi-typeahead-Persistence').click(); + cy.get('[data-cy=featuresSelect]').click(); cy.get('[data-cy=passivationSwitch]').next().click(); cy.get('[data-cy=connectionAttempts]').type(5); cy.get('[data-cy=connectionInterval]').type(60); @@ -57,9 +60,11 @@ describe('Cache Creation Wizard', () => { cy.get('#FileStore').click(); //Filling transactional cache properties - cy.get('#featuresSelect-select-multi-typeahead-typeahead').click().type('trans'); - cy.get('#TRANSACTIONAL > button').click(); + cy.get('[data-cy=featuresSelect]').click(); + cy.get('#select-multi-typeahead-Transactional').click(); + cy.get('[data-cy=featuresSelect]').click(); cy.get('#non_xa').click(); + cy.get('#pessimistic').click(); cy.get('[data-cy=wizardNextButton]').click(); diff --git a/src/app/CacheManagers/CacheTableDisplay.tsx b/src/app/CacheManagers/CacheTableDisplay.tsx index 0d5bb3a4e..b2717a414 100644 --- a/src/app/CacheManagers/CacheTableDisplay.tsx +++ b/src/app/CacheManagers/CacheTableDisplay.tsx @@ -61,7 +61,6 @@ import { onSearch } from '@app/utils/searchFilter'; import { DeleteCache } from '@app/Caches/DeleteCache'; import { IgnoreCache } from '@app/Caches/IgnoreCache'; import { SetAvailableCache } from '@app/Caches/SetAvailableCache'; -import { nil } from 'ajv'; interface CacheAction { cacheName: string; action: '' | 'ignore' | 'undo' | 'delete' | 'available'; @@ -91,6 +90,7 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb const isAdmin = ConsoleServices.security().hasConsoleACL(ConsoleACL.ADMIN, connectedUser); const isCreator = ConsoleServices.security().hasConsoleACL(ConsoleACL.CREATE, connectedUser); const canCreateCache = ConsoleServices.security().hasConsoleACL(ConsoleACL.CREATE, connectedUser); + const [rowsLoading, setRowsLoading] = useState(true); const [cachesPagination, setCachesPagination] = useState({ page: 1, @@ -120,7 +120,7 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb }; useEffect(() => { - if (caches) { + if (!loadingCaches) { const failedCaches = caches.reduce((failedCaches: string, cacheInfo: CacheInfo) => { if ( (cacheInfo.health as ComponentHealth) == ComponentHealth.FAILED || @@ -152,10 +152,10 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb }, [cachesPagination, filteredCaches]); useEffect(() => { - if (loadingCaches) { - setRows(null); + if (rows !=null) { + setRowsLoading(false); } - }, [loadingCaches]); + }, [rows]); useEffect(() => { setFilteredCaches(caches.filter((cache) => onSearch(searchValue, cache.name)).filter(onFilter)); @@ -732,7 +732,7 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb } const displayEmptyState = () => { - if (loadingCaches) { + if (rowsLoading) { return ( @@ -759,9 +759,10 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb ); }; + return ( - {caches.length == 0 ? ( + {!loadingCaches && !rowsLoading && caches.length == 0 ? ( emptyPage ) : ( @@ -778,7 +779,7 @@ const CacheTableDisplay = (props: { cmName: string; setCachesCount: (count: numb - {filteredCaches.length == 0 || rows == null || loadingCaches ? ( + {rowsLoading || rows == null ? ( {displayEmptyState()} diff --git a/src/app/Caches/Create/Features/SecuredCacheConfigurator.tsx b/src/app/Caches/Create/Features/SecuredCacheConfigurator.tsx index 20f6c0fe0..6f7be36a2 100644 --- a/src/app/Caches/Create/Features/SecuredCacheConfigurator.tsx +++ b/src/app/Caches/Create/Features/SecuredCacheConfigurator.tsx @@ -1,38 +1,29 @@ import React, { useEffect, useState } from 'react'; -import { FormGroup, FormHelperText, HelperText, HelperTextItem, SelectOptionProps } from '@patternfly/react-core'; +import { + Bullseye, + FormGroup, + FormHelperText, + HelperText, + HelperTextItem, + SelectOptionProps, Spinner, + Text, + TextContent +} from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; -import { ConsoleServices } from '@services/ConsoleServices'; import { useCreateCache } from '@app/services/createCacheHook'; import { FeatureCard } from '@app/Caches/Create/Features/FeatureCard'; import { CacheFeature } from '@services/infinispanRefData'; import { FeatureAlert } from '@app/Caches/Create/Features/FeatureAlert'; import { ExclamationCircleIcon } from '@patternfly/react-icons'; import { SelectMultiWithChips } from '@app/Common/SelectMultiWithChips'; +import { useFetchAvailableRolesNames } from '@app/services/rolesHook'; const SecuredCacheConfigurator = (props: { isEnabled: boolean }) => { const { configuration, setConfiguration } = useCreateCache(); const { t } = useTranslation(); const brandname = t('brandname.brandname'); const [roles, setRoles] = useState(configuration.feature.securedCache.roles); - const [loading, setLoading] = useState(true); - const [availableRoles, setAvailableRoles] = useState([]); - const [error, setError] = useState<'success' | 'error' | 'default'>('default'); - - useEffect(() => { - if (loading) { - ConsoleServices.security() - .getSecurityRolesNames() - .then((r) => { - if (r.isRight()) { - setAvailableRoles(r.value); - setError('success'); - } else { - setError('error'); - } - }) - .then(() => setLoading(false)); - } - }, [loading]); + const { availableRoleNames, loading, error } = useFetchAvailableRolesNames(); useEffect(() => { setConfiguration((prevState) => { @@ -62,7 +53,7 @@ const SecuredCacheConfigurator = (props: { isEnabled: boolean }) => { const rolesOptions = () : SelectOptionProps[] => { const selectOptions: SelectOptionProps[] = []; - availableRoles.forEach((role) => selectOptions.push({value: role, children: role})); + availableRoleNames.forEach((role) => selectOptions.push({value: role, children: role})); return selectOptions; }; @@ -71,15 +62,25 @@ const SecuredCacheConfigurator = (props: { isEnabled: boolean }) => { else setRoles([...roles, selection]); }; - if (!props.isEnabled) { + if (!props.isEnabled || error != '') { return ; } - return ( - + const buildContent = () => { + if (loading) { + return ( + + + + + {t('caches.create.configurations.feature.roles-loading')} + + + + ) + } + + return ( { )} + ); + } + return ( + + {buildContent()} ); }; diff --git a/src/app/assets/languages/en.json b/src/app/assets/languages/en.json index 6005f1dea..a099dc2a6 100644 --- a/src/app/assets/languages/en.json +++ b/src/app/assets/languages/en.json @@ -336,6 +336,7 @@ "index-sharding-tooltip": "Divide the index data into multiple smaller indexes called shards. Enabling sharding distributes data across multiple shards, optimizing system resources and enhancing search capabilities.", "secured": "Security authorization", "secured-description": "To protect data, restrict user access to the cache. When a user does not have one of the roles selected from the menu below, {{brandname}} denies cache operations.", + "roles-loading": "Loading available roles...", "select-roles": "Select roles", "select-roles-helper": "You must select at least one role.", "backups": "Backups for {{local_site_name}}", diff --git a/src/app/services/rolesHook.ts b/src/app/services/rolesHook.ts index 3b9e881fa..6330c5be9 100644 --- a/src/app/services/rolesHook.ts +++ b/src/app/services/rolesHook.ts @@ -3,6 +3,32 @@ import { ConsoleServices } from '@services/ConsoleServices'; import { useApiAlert } from '@utils/useApiAlert'; import { useTranslation } from 'react-i18next'; +export function useFetchAvailableRolesNames() { + const [availableRoleNames, setAvailableRoleNames] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + useEffect(() => { + if (loading) { + ConsoleServices.security() + .getSecurityRolesNames() + .then((either) => { + if (either.isRight()) { + setAvailableRoleNames(either.value); + } else { + setError(either.value.message); + } + }) + .then(() => setLoading(false)); + } + }, [loading]); + + return { + availableRoleNames, + loading, + error + }; +} + export function useFetchAvailableRoles() { const [roles, setRoles] = useState([]); const [loading, setLoading] = useState(true);