Skip to content

Commit

Permalink
🐛 Address targets falling out of sync after label selection changes (#…
Browse files Browse the repository at this point in the history
…2019)

Resolves: https://issues.redhat.com/browse/MTA-3180

Currently, the set-options screen allows edit of target labels which
can be set manually on the Advanced options step or by way of selecting
a target card on the target card step. This PR brings the form labels
and selectedTarget form values in sync by updating the selected targets
to reflect any form label changes in the set options page. This fixes
the mta-3180 scenario by causing validation to reflect the now accurate
state of the selectedTargets form value.

Signed-off-by: Ian Bolton <ibolton@redhat.com>
  • Loading branch information
ibolton336 authored Jul 22, 2024
1 parent 2ee9fe9 commit 80ee8ec
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 58 deletions.
40 changes: 26 additions & 14 deletions client/src/app/pages/applications/analysis-wizard/set-options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ import { DEFAULT_SELECT_MAX_HEIGHT } from "@app/Constants";
import { useFetchTargets } from "@app/queries/targets";
import defaultSources from "./sources";
import { QuestionCircleIcon } from "@patternfly/react-icons";
import {
findLabelBySelector,
isLabelInFormLabels,
updateSelectedTargetsBasedOnLabels,
} from "./utils";

export const SetOptions: React.FC = () => {
const { t } = useTranslation();
Expand All @@ -41,6 +46,7 @@ export const SetOptions: React.FC = () => {
excludedRulesTags,
autoTaggingEnabled,
advancedAnalysisEnabled,
selectedTargets,
} = watch();

const [isSelectTargetsOpen, setSelectTargetsOpen] = React.useState(false);
Expand Down Expand Up @@ -119,27 +125,33 @@ export const SetOptions: React.FC = () => {
isOpen={isSelectTargetsOpen}
onSelect={(_, selection) => {
const selectionWithLabelSelector = `konveyor.io/target=${selection}`;
const matchingLabel =
defaultTargetsAndTargetsLabels.find(
(label) => label.label === selectionWithLabelSelector
) || "";

const formLabelLabels = formLabels.map(
(formLabel) => formLabel.label
const matchingLabel = findLabelBySelector(
defaultTargetsAndTargetsLabels,
selectionWithLabelSelector
);
let updatedFormLabels = [];
if (
matchingLabel &&
!formLabelLabels.includes(matchingLabel.label)
!isLabelInFormLabels(formLabels, matchingLabel.label)
) {
onChange([...formLabels, matchingLabel]);
updatedFormLabels = [...formLabels, matchingLabel];
onChange(updatedFormLabels);
} else {
onChange(
formLabels.filter(
(formLabel) =>
formLabel.label !== selectionWithLabelSelector
)
updatedFormLabels = formLabels.filter(
(formLabel) =>
formLabel.label !== selectionWithLabelSelector
);
onChange(updatedFormLabels);
}

const updatedSelectedTargets =
updateSelectedTargetsBasedOnLabels(
updatedFormLabels,
selectedTargets,
targets
);
setValue("selectedTargets", updatedSelectedTargets);

onBlur();
setSelectTargetsOpen(!isSelectTargetsOpen);
}}
Expand Down
49 changes: 6 additions & 43 deletions client/src/app/pages/applications/analysis-wizard/set-targets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useFetchTargets } from "@app/queries/targets";
import { Application, TagCategory, Target } from "@app/api/models";
import { useFetchTagCategories } from "@app/queries/tags";
import { SimpleSelectCheckbox } from "@app/components/SimpleSelectCheckbox";
import { getUpdatedFormLabels, updateSelectedTargets } from "./utils";
interface SetTargetsProps {
applications: Application[];
}
Expand Down Expand Up @@ -101,61 +102,23 @@ export const SetTargets: React.FC<SetTargetsProps> = ({ applications }) => {
selectedLabelName: string,
target: Target
) => {
const updatedSelectedTargets = getUpdatedSelectedTargets(
isSelecting,
target
const updatedSelectedTargets = updateSelectedTargets(
target.id,
selectedTargets
);

const updatedFormLabels = getUpdatedFormLabels(
isSelecting,
selectedLabelName,
target
target,
formLabels
);

setValue("formLabels", updatedFormLabels);
setValue("selectedTargets", updatedSelectedTargets);
};

const getUpdatedSelectedTargets = (isSelecting: boolean, target: Target) => {
const { selectedTargets } = values;
if (isSelecting) {
return [...selectedTargets, target.id];
}
return selectedTargets.filter((id) => id !== target.id);
};

const getUpdatedFormLabels = (
isSelecting: boolean,
selectedLabelName: string,
target: Target
) => {
const { formLabels } = values;
if (target.custom) {
const customTargetLabelNames = target.labels?.map((label) => label.name);
const otherSelectedLabels = formLabels?.filter(
(formLabel) => !customTargetLabelNames?.includes(formLabel.name)
);
return isSelecting && target.labels
? [...otherSelectedLabels, ...target.labels]
: otherSelectedLabels;
} else {
const otherSelectedLabels = formLabels?.filter(
(formLabel) => formLabel.name !== selectedLabelName
);
if (isSelecting) {
const matchingLabel = target.labels?.find(
(label) => label.name === selectedLabelName
);
return matchingLabel
? [...otherSelectedLabels, matchingLabel]
: otherSelectedLabels;
}
return otherSelectedLabels;
}
};

const allProviders = targets.flatMap((target) => target.provider);

const languageOptions = Array.from(new Set(allProviders));

return (
Expand Down
79 changes: 78 additions & 1 deletion client/src/app/pages/applications/analysis-wizard/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from "react";
import { Application } from "@app/api/models";
import { Application, Target, TargetLabel } from "@app/api/models";
import { AnalysisMode, ANALYSIS_MODES } from "./schema";

export const isApplicationBinaryEnabled = (
Expand Down Expand Up @@ -60,3 +60,80 @@ export const useAnalyzableApplicationsByMode = (
),
[applications]
);

export const updateSelectedTargets = (
targetId: number,
selectedTargetIDs: number[]
) => {
const isSelected = selectedTargetIDs.includes(targetId);
return isSelected
? selectedTargetIDs.filter((id) => id !== targetId)
: [...selectedTargetIDs, targetId];
};

export const getUpdatedFormLabels = (
isSelecting: boolean,
selectedLabelName: string,
target: Target,
formLabels: TargetLabel[]
) => {
if (target.custom) {
const customTargetLabelNames = target.labels?.map((label) => label.name);
const otherSelectedLabels = formLabels?.filter(
(formLabel) => !customTargetLabelNames?.includes(formLabel.name)
);
return isSelecting && target.labels
? [...otherSelectedLabels, ...target.labels]
: otherSelectedLabels;
} else {
const otherSelectedLabels = formLabels?.filter(
(formLabel) => formLabel.name !== selectedLabelName
);
if (isSelecting) {
const matchingLabel = target.labels?.find(
(label) => label.name === selectedLabelName
);
return matchingLabel
? [...otherSelectedLabels, matchingLabel]
: otherSelectedLabels;
}
return otherSelectedLabels;
}
};
export const findLabelBySelector = (labels: TargetLabel[], selector: string) =>
labels.find((label) => label.label === selector) || "";

export const isLabelInFormLabels = (formLabels: TargetLabel[], label: string) =>
formLabels.some((formLabel) => formLabel.label === label);

export const labelToTargetId = (labelName: string, targets: Target[]) => {
const target = targets.find(
(t) => t.labels?.some((l) => l.name === labelName)
);
return target ? target.id : null;
};

export const updateSelectedTargetsBasedOnLabels = (
currentFormLabels: TargetLabel[],
selectedTargets: number[],
targets: Target[]
) => {
const newSelectedTargets = currentFormLabels.reduce(
(acc: number[], formLabel) => {
const targetId = labelToTargetId(formLabel.name, targets);
if (targetId && !acc.includes(targetId)) {
acc.push(targetId);
}
return acc;
},
[]
);

const filteredSelectedTargets = selectedTargets.filter((targetId) =>
currentFormLabels.some(
(formLabel) => labelToTargetId(formLabel.name, targets) === targetId
)
);

return [...new Set([...newSelectedTargets, ...filteredSelectedTargets])];
};

0 comments on commit 80ee8ec

Please sign in to comment.