Skip to content

Add provision to recipe parameters while enrolling applicaiton #1934

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@
"Resource label": "Resource label",
"Secure namespaces as per Recipe definition.": "Secure namespaces as per Recipe definition.",
"Recipe": "Recipe",
"Recipe parameters": "Recipe parameters",
"Recipe parameters are a set of named inputs that substitute the placeholders in a recipe.": "Recipe parameters are a set of named inputs that substitute the placeholders in a recipe.",
"Add parameter": "Add parameter",
"Value (optional)": "Value (optional)",
"Recipe list": "Recipe list",
"Only recipes of the selected namespaces will appear in the list.": "Only recipes of the selected namespaces will appear in the list.",
"Select a recipe": "Select a recipe",
Expand Down Expand Up @@ -147,6 +151,7 @@
"Type:": "Type:",
"Recipe name:": "Recipe name:",
"Recipe namespace:": "Recipe namespace:",
"Recipe Parameters:": "Recipe Parameters:",
"Label expressions:": "Label expressions:",
"PVC label selectors:": "PVC label selectors:",
"Replication": "Replication",
Expand Down Expand Up @@ -1304,7 +1309,6 @@
"Use different criteria for tagging your bucket.": "Use different criteria for tagging your bucket.",
"No tags are attached to this bucket.": "No tags are attached to this bucket.",
"Add tag": "Add tag",
"Value (optional)": "Value (optional)",
"Enter a valid rule name": "Enter a valid rule name",
"A rule name is required.": "A rule name is required.",
"A rule with this name already exists. Type a different name.": "A rule with this name already exists. Type a different name.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,28 @@ import {
ProtectionMethodType,
} from './reducer';

export const convertToRecipeParameters = (
params: Record<string, string>
): Record<string, string[]> => {
return Object.fromEntries(
Object.entries(params || {}).map(([key, value]) => [
key,
value
.split(',')
.map((v) => v.trim())
.filter(Boolean),
])
);
};

export const formatRecipeParametersForDisplay = (
params: Record<string, string[]>
): string => {
return Object.entries(params || {})
.map(([key, values]) => `${key}: ${values.join(', ')}`)
.join('; ');
};
Comment on lines +39 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const formatRecipeParametersForDisplay = (
params: Record<string, string[]>
): string => {
return Object.entries(params || {})
.map(([key, values]) => `${key}: ${values.join(', ')}`)
.join('; ');
};

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is for display only, let's keep it inside the review-step.tsx file.


export const getDRPCKindObj = (props: {
name: string;
preferredCluster: string;
Expand All @@ -31,10 +53,10 @@ export const getDRPCKindObj = (props: {
k8sResourceReplicationInterval: string;
recipeName?: string;
recipeNamespace?: string;
recipeParameters?: Record<string, string[]>;
k8sResourceLabelExpressions?: MatchExpression[];
pvcLabelExpressions?: MatchExpression[];
placementName: string;
recipeParameters?: Record<string, string[]>;
labels?: ObjectMetadata['labels'];
}): DRPlacementControlKind => ({
apiVersion: getAPIVersionForModel(DRPlacementControlModel),
Expand Down Expand Up @@ -106,7 +128,7 @@ export const createPromise = (
const { namespace, configuration, replication } = state;
const { clusterName, namespaces, name } = namespace;
const { protectionMethod, recipe, resourceLabels } = configuration;
const { recipeName, recipeNamespace } = recipe;
const { recipeName, recipeNamespace, recipeParameters } = recipe;
const { k8sResourceLabelExpressions, pvcLabelExpressions } = resourceLabels;
const { drPolicy, k8sResourceReplicationInterval } = replication;
const namespaceList = namespaces.map(getName);
Expand All @@ -132,6 +154,7 @@ export const createPromise = (
protectionMethod,
recipeName,
recipeNamespace,
recipeParameters: convertToRecipeParameters(recipeParameters),
k8sResourceLabelExpressions,
pvcLabelExpressions,
drPolicyName: getName(drPolicy),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export enum EnrollDiscoveredApplicationStateType {
SET_NAMESPACES = 'NAMESPACE/SET_NAMESPACES',
SET_PROTECTION_METHOD = 'CONFIGURATION/SET_PROTECTION_METHOD',
SET_RECIPE_NAME_NAMESPACE = 'CONFIGURATION/RECIPE/SET_RECIPE_NAME_NAMESPACE',
SET_RECIPE_PARAMETERS = 'SET_RECIPE_PARAMETERS',
SET_K8S_RESOURCE_LABEL_EXPRESSIONS = 'CONFIGURATION/RESOURCE_LABEL/SET_K8S_RESOURCE_LABEL_EXPRESSIONS',
SET_PVC_LABEL_EXPRESSIONS = 'CONFIGURATION/RESOURCE_LABEL/SET_PVC_LABEL_EXPRESSIONS',
SET_POLICY = 'REPLICATION/SET_POLICY',
Expand All @@ -40,6 +41,7 @@ export type EnrollDiscoveredApplicationState = {
recipeName: string;
// recipe CR namespace
recipeNamespace: string;
recipeParameters: Record<string, string>;
};
resourceLabels: {
k8sResourceLabelExpressions: MatchExpression[];
Expand Down Expand Up @@ -70,6 +72,7 @@ export const initialState: EnrollDiscoveredApplicationState = {
recipe: {
recipeName: '',
recipeNamespace: '',
recipeParameters: {},
},
resourceLabels: {
k8sResourceLabelExpressions: [],
Expand Down Expand Up @@ -112,6 +115,10 @@ export type EnrollDiscoveredApplicationAction =
type: EnrollDiscoveredApplicationStateType.SET_POLICY;
payload: DRPolicyKind;
}
| {
type: EnrollDiscoveredApplicationStateType.SET_RECIPE_PARAMETERS;
payload: Record<string, string>;
}
| {
type: EnrollDiscoveredApplicationStateType.SET_K8S_RESOURCE_REPLICATION_INTERVAL;
payload: string;
Expand Down Expand Up @@ -159,6 +166,18 @@ export const reducer: EnrollReducer = (state, action) => {
},
};
}
case EnrollDiscoveredApplicationStateType.SET_RECIPE_PARAMETERS: {
return {
...state,
configuration: {
...state.configuration,
recipe: {
...state.configuration.recipe,
recipeParameters: action.payload,
},
},
};
}
case EnrollDiscoveredApplicationStateType.SET_RECIPE_NAME_NAMESPACE: {
const [recipeName, recipeNamespace] = action.payload.split(
NAME_NAMESPACE_SPLIT_CHAR
Expand All @@ -168,6 +187,7 @@ export const reducer: EnrollReducer = (state, action) => {
configuration: {
...state.configuration,
recipe: {
...state.configuration.recipe,
recipeName,
recipeNamespace,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from 'react';
import { FieldLevelHelp, useCustomTranslation } from '@odf/shared';
import { LazyNameValueEditor } from '@odf/shared/utils/NameValueEditor';
import { FormGroup } from '@patternfly/react-core';
import {
EnrollDiscoveredApplicationAction,
EnrollDiscoveredApplicationStateType,
} from '../../utils/reducer';

export const RecipeParameterInput: React.FC<RecipeParameterInputProps> = ({
dispatch,
}) => {
const { t } = useCustomTranslation();
const [recipeParams, setRecipeParams] = React.useState<[string, string][]>([
['', ''],
]);

const updateRecipeParams = ({
nameValuePairs,
}: {
nameValuePairs: [string, string][];
}) => {
setRecipeParams(nameValuePairs);
const validPairs = nameValuePairs.filter(([key]) => key.trim());
dispatch({
type: EnrollDiscoveredApplicationStateType.SET_RECIPE_PARAMETERS,
payload: Object.fromEntries(validPairs.map(([k, v]) => [k.trim(), v])),
});
};

return (
<FormGroup
label={t('Recipe parameters')}
fieldId="recipe-parameters"
labelIcon={
<FieldLevelHelp>
{t(
'Recipe parameters are a set of named inputs that substitute the placeholders in a recipe.'
)}
</FieldLevelHelp>
}
data-test="recipe-parameters-form-group"
>
<LazyNameValueEditor
data-test="recipe-parameters"
nameValuePairs={recipeParams}
updateParentData={updateRecipeParams}
addString={t('Add parameter')}
valueString={t('Value (optional)')}
nameMaxLength={128}
valueMaxLength={256}
/>
</FormGroup>
);
};

type RecipeParameterInputProps = {
dispatch: React.Dispatch<EnrollDiscoveredApplicationAction>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
EnrollDiscoveredApplicationStateType,
} from '../../utils/reducer';
import './configuration-step.scss';
import { RecipeParameterInput } from './recipe-parameter-input';

const getRecipeOptions = (
searchResultItem: SearchResultItemType[]
Expand Down Expand Up @@ -117,6 +118,8 @@ export const RecipeSelection: React.FC<RecipeSelectionProps> = ({
) : (
<StatusBox loaded={searchLoaded} loadError={searchError} />
)}

{recipeNameNamespace && <RecipeParameterInput dispatch={dispatch} />}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import {
} from '@odf/shared/review-and-create-step';
import { getName } from '@odf/shared/selectors';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import * as _ from 'lodash-es';
import {
formatRecipeParametersForDisplay,
convertToRecipeParameters,
} from '../../utils/k8s-utils';
import {
EnrollDiscoveredApplicationState,
ProtectionMethodType,
Expand All @@ -23,7 +28,7 @@ export const Review: React.FC<ReviewProps> = ({ state }) => {
const { namespace, configuration, replication } = state;
const { clusterName, namespaces, name } = namespace;
const { protectionMethod, recipe, resourceLabels } = configuration;
const { recipeName, recipeNamespace } = recipe;
const { recipeName, recipeNamespace, recipeParameters } = recipe;
const { k8sResourceLabelExpressions, pvcLabelExpressions } = resourceLabels;
const { drPolicy, k8sResourceReplicationInterval } = replication;

Expand All @@ -37,6 +42,9 @@ export const Review: React.FC<ReviewProps> = ({ state }) => {
drPolicy.spec.schedulingInterval === '0m'
? REPLICATION_DISPLAY_TEXT(t).sync
: REPLICATION_DISPLAY_TEXT(t).async;
const displayRecipeParameters = formatRecipeParametersForDisplay(
convertToRecipeParameters(recipeParameters)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here,
The parameters are already comma-separated strings,
convertToRecipeParameters is converting the parameter into a list, and formatRecipeParametersForDisplay is again converting it back to comma comma-separated string.

This looks redundant, I think only the whitespace trimming logic has to be common between these two functions.

);
const [unitVal, interval] = parseSyncInterval(k8sResourceReplicationInterval);

return (
Expand All @@ -62,6 +70,11 @@ export const Review: React.FC<ReviewProps> = ({ state }) => {
<ReviewAndCreationItem label={t('Recipe namespace:')}>
{recipeNamespace}
</ReviewAndCreationItem>
{!_.isEmpty(recipeParameters) && (
<ReviewAndCreationItem label={t('Recipe Parameters:')}>
{displayRecipeParameters}
</ReviewAndCreationItem>
)}
</>
)}
{protectionMethod === ProtectionMethodType.RESOURCE_LABEL && (
Expand Down