diff --git a/src/AppContext.tsx b/src/AppContext.tsx index 0d0a559..7e9291d 100644 --- a/src/AppContext.tsx +++ b/src/AppContext.tsx @@ -1,5 +1,6 @@ import { createContext } from 'react'; import { Domain } from './Api'; +import { VerifyState } from './Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry'; /** * It represents the application context so common events and properties @@ -17,6 +18,11 @@ export interface IAppContext { getToken: () => string; /** Set the value of the token. */ setToken: (value: string) => void; + /** Retrieve the value of the registered status which is updated once + * the user has registered the domain by using ipa-hcc tool. */ + getRegisteredStatus: () => VerifyState; + /** Setter for the registered status. */ + setRegisteredStatus: (value: VerifyState) => void; /** Get the ephemeral domain state that manage the wizard. */ getDomain: () => Domain; /** Set the ephemeral domain information. */ @@ -40,6 +46,12 @@ export const AppContext = createContext({ setToken: (value: string) => { throw new Error('Function "setToken" not implemented: value=' + value); }, + getRegisteredStatus: (): VerifyState => { + return 'initial'; + }, + setRegisteredStatus: (value: VerifyState) => { + throw new Error('Function "setRegisteredStatus" not implemented: value=' + value); + }, getDomain: (): Domain => { return {} as Domain; }, diff --git a/src/AppEntry.tsx b/src/AppEntry.tsx index 4fe8379..557347b 100644 --- a/src/AppEntry.tsx +++ b/src/AppEntry.tsx @@ -7,12 +7,14 @@ import { getBaseName } from '@redhat-cloud-services/frontend-components-utilitie import logger from 'redux-logger'; import { AppContext } from './AppContext'; import { Domain } from './Api'; +import { VerifyState } from './Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry'; const AppEntry = () => { const appContext = useContext(AppContext); const [domains, setDomains] = useState([]); const [wizardToken, setWizardToken] = useState(''); const [wizardDomain, setWizardDomain] = useState({} as Domain); + const [wizardRegisterStatus, setWizardRegisterStatus] = useState('initial'); const cbSetDomains = (domains: Domain[]) => { appContext.domains = domains; setDomains(domains); @@ -29,6 +31,12 @@ const AppEntry = () => { const cbSetWizardDomain = (value: Domain) => { setWizardDomain(value); }; + const cbGetRegisterStatus = (): VerifyState => { + return wizardRegisterStatus; + }; + const cbSetRegisterStatus = (value: VerifyState) => { + setWizardRegisterStatus(value); + }; return ( @@ -39,6 +47,8 @@ const AppEntry = () => { wizard: { getToken: cbGetWizardToken, setToken: cbSetWizardToken, + getRegisteredStatus: cbGetRegisterStatus, + setRegisteredStatus: cbSetRegisterStatus, getDomain: cbGetWizardDomain, setDomain: cbSetWizardDomain, }, diff --git a/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.scss b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.scss new file mode 100644 index 0000000..acd9576 --- /dev/null +++ b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.scss @@ -0,0 +1 @@ +@import '~@redhat-cloud-services/frontend-components-utilities/styles/variables'; diff --git a/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.test.tsx b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.test.tsx new file mode 100644 index 0000000..ad7d07b --- /dev/null +++ b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.test.tsx @@ -0,0 +1,10 @@ +// import React from 'react'; +// import { render } from '@testing-library/react'; +// import Page1 from './Page1'; +import '@testing-library/jest-dom'; + +test('expect VerifyRegistry to render children', () => { + // render(); + // expect(screen.getByRole('heading')).toHaveTextContent('Hello'); + return; +}); diff --git a/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.tsx b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.tsx new file mode 100644 index 0000000..90305a1 --- /dev/null +++ b/src/Routes/WizardPage/Components/VerifyRegistry/VerifyRegistry.tsx @@ -0,0 +1,263 @@ +import React, { useEffect, useState } from 'react'; + +import { AxiosError } from 'axios'; +import { Button, Icon, Stack, StackItem, TextContent } from '@patternfly/react-core'; +import { CheckCircleIcon } from '@patternfly/react-icons/dist/esm/icons/check-circle-icon'; +import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon'; +import { PendingIcon } from '@patternfly/react-icons/dist/esm/icons/pending-icon'; +import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon'; + +import './VerifyRegistry.scss'; +import { Domain, ResourcesApiFactory } from '../../../../Api'; + +/* Common definitions */ + +export type VerifyState = 'initial' | 'waiting' | 'timed-out' | 'not-found' | 'completed'; + +/* VerifyRegistryIcon component */ + +interface VerifyRegistryIconProps { + state: VerifyState; +} + +const VerifyRegistryIcon = (props: VerifyRegistryIconProps) => { + return ( + <> + {props.state == 'initial' && ( + + + + )} + {props.state == 'waiting' && ( + + + + )} + {props.state == 'timed-out' && ( + + + + )} + {props.state == 'not-found' && ( + + + + )} + {props.state == 'completed' && ( + + + + )} + + ); +}; + +/* VerifyRegistryLabel component */ + +interface VerifyRegistryLabelProps { + state: VerifyState; +} + +const VerifyRegistryLabel = (props: VerifyRegistryLabelProps) => { + return ( + <> + {props.state == 'initial' && Verify registration} + {props.state == 'waiting' && Verify registration} + {props.state == 'timed-out' && Verify registration} + {props.state == 'not-found' && Verify registration} + {props.state == 'completed' && Verify registration} + + ); +}; + +/* VerifyRegistryLabel component */ + +interface VerifyRegistryDescriptionProps { + state: VerifyState; +} + +const VerifyRegistryDescription = (props: VerifyRegistryDescriptionProps) => { + return ( + <> + {props.state == 'initial' && Running verification test} + {props.state == 'waiting' && Waiting for registration data} + {props.state == 'timed-out' && Test timed out} + {props.state == 'not-found' && Registration data not found} + {props.state == 'completed' && Test completed} + + ); +}; + +/* VerifyRegistryyFooter */ + +interface VerifyRegistryFooterProps { + state: VerifyState; + onTest?: () => void; +} + +const VerifyRegistryFooter = (props: VerifyRegistryFooterProps) => { + const linkTroubleshootRegistration = 'https://www.google.com/search?q=freeipa+troubleshooting'; + return ( + <> + {props.state == 'initial' && ( + <> + + + )} + {props.state == 'waiting' && <>} + {props.state == 'timed-out' && ( + <> + + + )} + {props.state == 'not-found' && ( + <> + + + )} + {props.state == 'completed' && ( + <> + + + )} + + ); +}; + +/* VerifyRegistry component */ + +interface VerifyRegistryProps { + state: VerifyState; + uuid: string; + onChange: (newState: VerifyState, domain?: Domain) => void; +} + +const VerifyRegistry = (props: VerifyRegistryProps) => { + const [isPolling, setIsPolling] = useState(true); + + const base_url = '/api/idmsvc/v1'; + const resources_api = ResourcesApiFactory(undefined, base_url, undefined); + const timeout = 3 * 1000; // Seconds + + /** TODO Extract this effect in a hook to simplify this code */ + useEffect(() => { + let intervalId: NodeJS.Timeout | null = null; + let elapsedTime = 0; + let newState: VerifyState = props.state; + let domain: Domain | undefined = undefined; + const stopPolling = (state: VerifyState, domain?: Domain) => { + if (intervalId) { + clearInterval(intervalId); + } + intervalId = null; + setIsPolling(false); + if (state === 'completed') { + props.onChange(state, domain); + } else { + props.onChange(state); + } + }; + if (!isPolling) { + return; + } + const fetchData = async () => { + try { + const response = await resources_api.readDomain(props.uuid, undefined, undefined); + newState = 'completed'; + domain = response.data; + newState = 'completed'; + } catch (error) { + const axiosError = error as AxiosError; + switch (axiosError.code) { + case AxiosError.ECONNABORTED: + case AxiosError.ERR_BAD_OPTION: + case AxiosError.ERR_BAD_OPTION_VALUE: + case AxiosError.ERR_CANCELED: + newState = 'waiting'; + break; + case AxiosError.ERR_DEPRECATED: + case AxiosError.ERR_FR_TOO_MANY_REDIRECTS: + case AxiosError.ERR_NETWORK: + case AxiosError.ETIMEDOUT: + newState = 'timed-out'; + break; + case AxiosError.ERR_BAD_REQUEST: + case AxiosError.ERR_BAD_RESPONSE: + default: + newState = 'waiting'; + break; + } + } + + if (newState !== undefined && newState !== props.state) { + switch (newState) { + case 'timed-out': + case 'waiting': + props.onChange(newState); + // setState(newState); + break; + default: + if (elapsedTime >= timeout) { + newState = 'timed-out'; + stopPolling(newState); + } + break; + case 'completed': + stopPolling(newState, domain); + break; + } + } + elapsedTime += 1000; // Increase elapsed time by 1 second + if (elapsedTime > timeout) { + newState = 'timed-out'; + stopPolling(newState); + } + }; + + fetchData(); + intervalId = setInterval(fetchData, 1000); + + return () => { + if (intervalId) { + clearInterval(intervalId); + } + }; + }, [isPolling]); + + const onRetry = () => { + props.onChange('initial'); + // setState('initial'); + setIsPolling(true); + }; + + return ( + + + + + + + + + + + + + + + + + ); +}; + +// VerifyRegistry.defaultProps = defaultVerifyRegistryProps; + +export default VerifyRegistry;