Skip to content

Commit

Permalink
Merge pull request #1240 from GowthamShanmugam/RHSTOR-4954_drpc_name_…
Browse files Browse the repository at this point in the history
…input

Custom name input field for the protected imperative application
  • Loading branch information
openshift-merge-bot[bot] authored Mar 21, 2024
2 parents 738a4ef + 5cd7380 commit 28d84a0
Show file tree
Hide file tree
Showing 17 changed files with 260 additions and 102 deletions.
9 changes: 6 additions & 3 deletions locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@
"There are no namespaces to display. Select a cluster first to view namespaces.": "There are no namespaces to display. Select a cluster first to view namespaces.",
"There are no namespaces to display.": "There are no namespaces to display.",
"This list does not include namespaces where applications are enrolled separately under disaster recovery protection.": "This list does not include namespaces where applications are enrolled separately under disaster recovery protection.",
"Name requirements": "Name requirements",
"Example": "Example",
"Enter a unique name": "Enter a unique name",
"Name input": "Name input",
"A unique identifier for ACM discovered applications from selected namespaces.": "A unique identifier for ACM discovered applications from selected namespaces.",
"Select a DRCluster to choose your namespace.": "Select a DRCluster to choose your namespace.",
"No DR cluster found": "No DR cluster found",
"Namespace selection": "Namespace selection",
Expand Down Expand Up @@ -422,8 +427,6 @@
"Create BlockPool": "Create BlockPool",
"Data loss may occur, only recommended for small clusters or when backups are available or data loss is acceptable": "Data loss may occur, only recommended for small clusters or when backups are available or data loss is acceptable",
"{{replica}} Replication": "{{replica}} Replication",
"Name requirements": "Name requirements",
"Example": "Example",
"pool-name-help": "pool-name-help",
"my-block-pool": "my-block-pool",
"Data protection policy": "Data protection policy",
Expand Down Expand Up @@ -980,8 +983,8 @@
"Access Key Field": "Access Key Field",
"Secret Key Field": "Secret Key Field",
"ObjectBucketClaim Name": "ObjectBucketClaim Name",
"If not provided a generic name will be generated.": "If not provided a generic name will be generated.",
"my-object-bucket": "my-object-bucket",
"If not provided a generic name will be generated.": "If not provided a generic name will be generated.",
"StorageClass": "StorageClass",
"Defines the object-store service and the bucket provisioner.": "Defines the object-store service and the bucket provisioner.",
"BucketClass": "BucketClass",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,18 +265,59 @@ jest.mock(
})
);

// Mocking as "fireEvent" is throwing warning for FieldLevelHelp
jest.mock('@odf/shared/generic', () => ({
...jest.requireActual('@odf/shared/generic'),
FieldLevelHelp: () => <></>,
// Mocking as "Popover" is throwing warning for FieldLevelHelp & TextInputWithFieldRequirements
jest.mock('@patternfly/react-core', () => ({
...jest.requireActual('@patternfly/react-core'),
Popover: () => <></>,
}));

const moveToStep = async (step: number) => {
if (step > 1) {
// Select cluster
fireEvent.click(screen.getByText('Select cluster'));
fireEvent.click(screen.getByText('east-1'));

// Select namespaces
fireEvent.click(screen.getByLabelText('Select row 0'));
fireEvent.click(screen.getByLabelText('Select row 1'));

// Name input
fireEvent.change(screen.getByLabelText('Name input'), {
target: { value: 'my-name' },
});
await waitFor(() => {
expect(screen.getByDisplayValue('my-name')).toBeInTheDocument();
});

// Next wizard step
fireEvent.click(screen.getByText('Next'));
}

if (step > 2) {
// Select recipe
fireEvent.click(screen.getByText('Select a recipe'));
fireEvent.click(screen.getByText('mock-recipe-1'));

// Next wizard step
fireEvent.click(screen.getByText('Next'));
}

if (step > 3) {
// Select policy
fireEvent.click(screen.getByText('Select a policy'));
fireEvent.click(screen.getByText('mock-policy-1'));

// Next wizard step
fireEvent.click(screen.getByText('Next'));
}
};

describe('Test namespace step', () => {
beforeEach(() => {
testCase += 1;
render(<EnrollDiscoveredApplication />);
});
test('Namespace selection form test', async () => {
testCase = 1;
// Step1 title
expect(screen.getByText('Namespace selection')).toBeInTheDocument();
// Step1 title description
Expand Down Expand Up @@ -311,6 +352,13 @@ describe('Test namespace step', () => {
'This list does not include namespaces where applications are enrolled separately under disaster recovery protection.'
)
).toBeInTheDocument();
// Name input
expect(screen.getByText('Name')).toBeInTheDocument();
expect(
screen.getByText(
'A unique identifier for ACM discovered applications from selected namespaces.'
)
).toBeInTheDocument();

// Footer
expect(screen.getByText('Next')).toBeInTheDocument();
Expand All @@ -331,6 +379,7 @@ describe('Test namespace step', () => {
});

test('No namespace found test', async () => {
testCase = 2;
// Cluster selection
fireEvent.click(screen.getByText('Select cluster'));
fireEvent.click(screen.getByText('east-1'));
Expand All @@ -347,6 +396,7 @@ describe('Test namespace step', () => {
});

test('Namespace selection test', async () => {
testCase = 3;
// Cluster east-1 selection
fireEvent.click(screen.getByText('Select cluster'));
fireEvent.click(screen.getByText('east-1'));
Expand Down Expand Up @@ -384,28 +434,27 @@ describe('Test namespace step', () => {
expect(() => screen.getByText('openshift')).toThrow(
'Unable to find an element'
);
// Name input
fireEvent.change(screen.getByLabelText('Name input'), {
target: { value: 'my-name' },
});
await waitFor(() => {
expect(screen.getByDisplayValue('my-name')).toBeInTheDocument();
});
});
});

describe('Test configure step', () => {
beforeEach(() => {
testCase += 1;
render(<EnrollDiscoveredApplication />);
// Select cluster
fireEvent.click(screen.getByText('Select cluster'));
fireEvent.click(screen.getByText('east-1'));

// Select namespaces
fireEvent.click(screen.getByLabelText('Select row 0'));
fireEvent.click(screen.getByLabelText('Select row 1'));

// Next wizard step
fireEvent.click(screen.getByText('Next'));
});

test('Configure form test', async () => {
// Step1 title
testCase = 4;
await moveToStep(2);
// Step2 title
expect(screen.getByText('Configure definition')).toBeInTheDocument();
// Step1 title description
// Step2 title description
expect(
screen.getByText(
'Choose your configuration preference to protect resources (application volumes/PVCs, or Kubernetes objects).'
Expand Down Expand Up @@ -462,32 +511,16 @@ describe('Test configure step', () => {

describe('Test replication step', () => {
beforeEach(() => {
testCase += 1;
render(<EnrollDiscoveredApplication />);
// Select cluster
fireEvent.click(screen.getByText('Select cluster'));
fireEvent.click(screen.getByText('east-1'));

// Select namespaces
fireEvent.click(screen.getByLabelText('Select row 0'));
fireEvent.click(screen.getByLabelText('Select row 1'));

// Next wizard step
fireEvent.click(screen.getByText('Next'));

// Select recipe
fireEvent.click(screen.getByText('Select a recipe'));
fireEvent.click(screen.getByText('mock-recipe-1'));

// Next wizard step
fireEvent.click(screen.getByText('Next'));
});
test('Replication form test', async () => {
// Step1 title
testCase = 5;
await moveToStep(3);
// Step3 title
expect(
screen.getByText('Volume and Kubernetes object replication')
).toBeInTheDocument();
// Step1 title description
// Step3 title description
expect(
screen.getByText(
'Define where to sync or replicate your application volumes and Kubernetes object using a disaster recovery policy.'
Expand Down Expand Up @@ -544,38 +577,11 @@ describe('Test replication step', () => {
});
describe('Test review step', () => {
beforeEach(() => {
testCase += 1;
render(<EnrollDiscoveredApplication />);
// Select cluster
fireEvent.click(screen.getByText('Select cluster'));
fireEvent.click(screen.getByText('east-1'));

// Select namespaces
fireEvent.click(screen.getByLabelText('Select row 0'));
fireEvent.click(screen.getByLabelText('Select row 1'));

// Next wizard step
fireEvent.click(screen.getByText('Next'));

if (testCase === 6) {
// Select recipe
fireEvent.click(screen.getByText('Select a recipe'));
fireEvent.click(screen.getByText('mock-recipe-1'));

// Next wizard step
fireEvent.click(screen.getByText('Next'));
} else {
// ToDo: Resource label selection reivew
}

// Select policy
fireEvent.click(screen.getByText('Select a policy'));
fireEvent.click(screen.getByText('mock-policy-1'));

// Next wizard step
fireEvent.click(screen.getByText('Next'));
});
test('Review form test', async () => {
testCase = 6;
await moveToStep(4);
// Namespace selection test
expect(screen.getAllByText('Namespace').length === 2).toBeTruthy();
expect(screen.getByText('Cluster:')).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import {
} from './utils/reducer';

const validateNamespaceStep = (state: EnrollDiscoveredApplicationState) =>
!!state.namespace.clusterName && !!state.namespace.namespaces.length;
!!state.namespace.clusterName &&
!!state.namespace.namespaces.length &&
!!state.namespace.name;

const validateConfigurationStep = (state: EnrollDiscoveredApplicationState) => {
const { recipe, resourceLabels, protectionMethod } = state.configuration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export enum EnrollDiscoveredApplicationStateType {
SET_PVC_LABEL_EXPRESSIONS = 'CONFIGURATION/RESOURCE_LABEL/SET_PVC_LABEL_EXPRESSIONS',
SET_POLICY = 'REPLICATION/SET_POLICY',
SET_K8S_RESOURCE_REPLICATION_INTERVAL = 'REPLICATION/SET_K8S_RESOURCE_REPLICATION_INTERVAL',
SET_NAME = 'NAMESPACE/SET_NAME',
}

export type EnrollDiscoveredApplicationState = {
Expand All @@ -29,6 +30,8 @@ export type EnrollDiscoveredApplicationState = {
namespaces: K8sResourceCommon[];
// Cluster name of the discovered application
clusterName: string;
// DRPC name to protect the discovered application
name: string;
};
configuration: {
// recipe CRD (or) normal K8s CR label based protection
Expand Down Expand Up @@ -60,6 +63,7 @@ export const initialState: EnrollDiscoveredApplicationState = {
namespace: {
clusterName: '',
namespaces: [],
name: '',
},
configuration: {
protectionMethod: ProtectionMethodType.RECIPE,
Expand Down Expand Up @@ -111,6 +115,10 @@ export type EnrollDiscoveredApplicationAction =
| {
type: EnrollDiscoveredApplicationStateType.SET_K8S_RESOURCE_REPLICATION_INTERVAL;
payload: string;
}
| {
type: EnrollDiscoveredApplicationStateType.SET_NAME;
payload: string;
};

export const reducer: EnrollReducer = (state, action) => {
Expand All @@ -127,6 +135,7 @@ export const reducer: EnrollReducer = (state, action) => {
namespace: {
...newState.namespace,
clusterName: action.payload,
name: state.namespace.name,
},
};
}
Expand Down Expand Up @@ -207,6 +216,15 @@ export const reducer: EnrollReducer = (state, action) => {
},
};
}
case EnrollDiscoveredApplicationStateType.SET_NAME: {
return {
...state,
namespace: {
...state.namespace,
name: action.payload,
},
};
}
default:
throw new TypeError(`${action} is not a valid reducer action`);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import * as React from 'react';
import {
getDRPlacementControlResourceObj,
useACMSafeFetch,
} from '@odf/mco/hooks';
import { useACMSafeFetch } from '@odf/mco/hooks';
import { DRPlacementControlKind, DRPolicyKind } from '@odf/mco/types';
import {
queryNamespacesUsingCluster,
Expand All @@ -21,7 +18,6 @@ import { isSystemNamespace, sortRows } from '@odf/shared/utils';
import {
K8sResourceCommon,
ListPageFilter,
useK8sWatchResource,
useListPageFilter,
} from '@openshift-console/dynamic-plugin-sdk';
import { TFunction } from 'i18next';
Expand Down Expand Up @@ -79,13 +75,16 @@ const TableRow: React.FC<RowComponentType<K8sResourceCommon>> = ({
};

export const NamespaceSelectionTable: React.FC<NamespaceSelectionTableProps> =
({ namespaces, clusterName, policies, isValidationEnabled, dispatch }) => {
({
namespaces,
clusterName,
policies,
drPlacements,
isValidationEnabled,
dispatch,
}) => {
const { t } = useCustomTranslation();

const [drPlacements, drpcLoaded, drpcLoadError] = useK8sWatchResource<
DRPlacementControlKind[]
>(getDRPlacementControlResourceObj());

const protectedNamespaces = React.useMemo(() => {
const eligiblePolicies = findAllEligiblePolicies(clusterName, policies);
return getProtectedNamespaces(drPlacements, eligiblePolicies);
Expand All @@ -100,11 +99,8 @@ export const NamespaceSelectionTable: React.FC<NamespaceSelectionTableProps> =
const [searchResult, searchError, searchLoaded] =
useACMSafeFetch(searchQuery);

const loaded = searchLoaded && drpcLoaded;
const loadError = searchError || drpcLoadError;

const userNamespaces: K8sResourceCommon[] = React.useMemo(() => {
if (loaded && !loadError) {
if (searchLoaded && !searchError) {
// Converting from search result type to K8sResourceCommon for shared components compatibility.
const allNamespaces = convertSearchResultToK8sResourceCommon(
searchResult?.data.searchResult?.[0]?.items || []
Expand All @@ -119,7 +115,7 @@ export const NamespaceSelectionTable: React.FC<NamespaceSelectionTableProps> =
});
}
return [];
}, [searchResult, protectedNamespaces, loaded, loadError]);
}, [searchResult, protectedNamespaces, searchLoaded, searchError]);

const [data, filteredData, onFilterChange] =
useListPageFilter(userNamespaces);
Expand Down Expand Up @@ -147,7 +143,7 @@ export const NamespaceSelectionTable: React.FC<NamespaceSelectionTableProps> =
<GridItem span={10}>
<ListPageFilter
data={data}
loaded={loaded}
loaded={searchLoaded}
onFilterChange={onFilterChange}
/>
{namespaceValidated && (
Expand Down Expand Up @@ -190,6 +186,7 @@ type NamespaceSelectionTableProps = {
namespaces: K8sResourceCommon[];
clusterName: string;
policies: DRPolicyKind[];
drPlacements: DRPlacementControlKind[];
isValidationEnabled: boolean;
dispatch: React.Dispatch<EnrollDiscoveredApplicationAction>;
};
Loading

0 comments on commit 28d84a0

Please sign in to comment.