diff --git a/frontend/app/src/components/form/node-form.tsx b/frontend/app/src/components/form/node-form.tsx index b870b91125..ed8d177980 100644 --- a/frontend/app/src/components/form/node-form.tsx +++ b/frontend/app/src/components/form/node-form.tsx @@ -38,6 +38,7 @@ export type NodeFormProps = { onSuccess?: (newObject: any) => void; currentObject?: Record; isFilterForm?: boolean; + isUpdate?: boolean; onSubmit?: (data: NodeFormSubmitParams) => void; }; @@ -49,6 +50,7 @@ export const NodeForm = ({ onSuccess, isFilterForm, onSubmit, + isUpdate, ...props }: NodeFormProps) => { const branch = useAtomValue(currentBranchAtom); @@ -80,6 +82,7 @@ export const NodeForm = ({ isFilterForm, filters, pools: numberPools, + isUpdate, }); async function onSubmitCreate(data: Record) { diff --git a/frontend/app/src/components/form/object-form.tsx b/frontend/app/src/components/form/object-form.tsx index 9e21fa4f4e..72ded9f3d3 100644 --- a/frontend/app/src/components/form/object-form.tsx +++ b/frontend/app/src/components/form/object-form.tsx @@ -25,6 +25,7 @@ export interface ObjectFormProps extends Omit; currentProfiles?: ProfileData[]; isFilterForm?: boolean; + isUpdate?: boolean; onSubmit?: (data: NodeFormSubmitParams) => void; onUpdateComplete?: () => void; } diff --git a/frontend/app/src/components/form/utils/getFormFieldsFromSchema.ts b/frontend/app/src/components/form/utils/getFormFieldsFromSchema.ts index 623ca41d13..c2b7836ad7 100644 --- a/frontend/app/src/components/form/utils/getFormFieldsFromSchema.ts +++ b/frontend/app/src/components/form/utils/getFormFieldsFromSchema.ts @@ -17,11 +17,7 @@ import { FormFieldValue, NumberPoolData, } from "@/components/form/type"; -import { - getObjectRelationshipsForForm, - getOptionsFromAttribute, - getRelationshipOptions, -} from "@/utils/getSchemaObjectColumns"; +import { getOptionsFromAttribute, getRelationshipOptions } from "@/utils/getSchemaObjectColumns"; import { isGeneric, sortByOrderWeight } from "@/utils/common"; import { getFieldDefaultValue } from "@/components/form/utils/getFieldDefaultValue"; import { SchemaAttributeType } from "@/screens/edit-form-hook/dynamic-control-types"; @@ -33,6 +29,7 @@ import { getRelationshipDefaultValue } from "@/components/form/utils/getRelation import { Filter } from "@/hooks/useFilters"; import { getRelationshipParent } from "@/components/form/utils/getRelationshipParent"; import { isRequired } from "@/components/form/utils/validation"; +import { getRelationshipsForForm } from "@/components/form/utils/getRelationshipsForForm"; type GetFormFieldsFromSchema = { schema: iNodeSchema | iGenericSchema; @@ -42,6 +39,7 @@ type GetFormFieldsFromSchema = { isFilterForm?: boolean; filters?: Array; pools?: Array; + isUpdate?: boolean; }; export const getFormFieldsFromSchema = ({ @@ -52,10 +50,11 @@ export const getFormFieldsFromSchema = ({ isFilterForm, filters, pools = [], + isUpdate, }: GetFormFieldsFromSchema): Array => { const unorderedFields = [ ...(schema.attributes ?? []), - ...getObjectRelationshipsForForm(schema), + ...getRelationshipsForForm(schema.relationships ?? [], isUpdate), ].filter((attribute) => !attribute.read_only); const orderedFields: typeof unorderedFields = sortByOrderWeight(unorderedFields); diff --git a/frontend/app/src/components/form/utils/getRelationshipsForForm.ts b/frontend/app/src/components/form/utils/getRelationshipsForForm.ts new file mode 100644 index 0000000000..efffb958f0 --- /dev/null +++ b/frontend/app/src/components/form/utils/getRelationshipsForForm.ts @@ -0,0 +1,19 @@ +import { components } from "@/infraops"; +import { peersKindForForm } from "@/config/constants"; + +export const getRelationshipsForForm = ( + relationships: components["schemas"]["RelationshipSchema-Output"][], + isUpdate?: boolean +) => { + // Filter relationships based on cardinality and kind for form inclusion + // For create forms, include relationships with cardinality 'one', eligible kinds, or mandatory cardinality 'many' + // For update forms, only include relationships with cardinality 'one' or those with eligible kinds (Attribute or Parent). Other should be display as tabs on details view + return relationships.filter((relationship) => { + if (relationship.cardinality === "one") return true; + + const isPeerKindEligibleForForm = peersKindForForm.includes(relationship?.kind ?? ""); + if (isUpdate) return isPeerKindEligibleForForm; + + return isPeerKindEligibleForForm || !relationship.optional; + }); +}; diff --git a/frontend/app/src/screens/object-item-edit/object-item-edit-paginated.tsx b/frontend/app/src/screens/object-item-edit/object-item-edit-paginated.tsx index e1f7d3f007..534f6f6b8c 100644 --- a/frontend/app/src/screens/object-item-edit/object-item-edit-paginated.tsx +++ b/frontend/app/src/screens/object-item-edit/object-item-edit-paginated.tsx @@ -137,6 +137,7 @@ export default function ObjectItemEditComponent(props: Props) { currentObject={objectDetailsData} currentProfiles={objectProfiles} data-cy="object-item-edit" + isUpdate /> ); } diff --git a/frontend/app/src/utils/getSchemaObjectColumns.ts b/frontend/app/src/utils/getSchemaObjectColumns.ts index 114942a436..f2a88dc206 100644 --- a/frontend/app/src/utils/getSchemaObjectColumns.ts +++ b/frontend/app/src/utils/getSchemaObjectColumns.ts @@ -1,7 +1,6 @@ import { attributesKindForDetailsViewExclude, attributesKindForListView, - peersKindForForm, relationshipsForDetailsView, relationshipsForListView, relationshipsForTabs, @@ -143,25 +142,6 @@ export const getObjectTabs = (tabs: any[], data: any) => { })); }; -// Used by the form to display the fields -export const getObjectRelationshipsForForm = ( - schema?: iNodeSchema | iGenericSchema, - isUpdate?: boolean -) => { - const relationships = (schema?.relationships || []) - // Create form includes cardinality many but only if required, edit form doesn't include it at all - .filter( - (relationship) => - relationship.cardinality === "one" || - (isUpdate - ? peersKindForForm.includes(relationship?.kind ?? "") - : peersKindForForm.includes(relationship?.kind ?? "") || !relationship.optional) - ) - .filter(Boolean); - - return relationships; -}; - // Include current value in the options to make it available in the select component export const getRelationshipOptions = (row: any, field: any, schemas: any[], generics: any[]) => { const value = row && (row[field.name]?.node ?? row[field.name]); diff --git a/frontend/app/tests/unit/components/form/utils/getRelationshipsForForm.test.ts b/frontend/app/tests/unit/components/form/utils/getRelationshipsForForm.test.ts new file mode 100644 index 0000000000..4509a6d363 --- /dev/null +++ b/frontend/app/tests/unit/components/form/utils/getRelationshipsForForm.test.ts @@ -0,0 +1,97 @@ +import { describe, expect, it } from "vitest"; +import { getRelationshipsForForm } from "@/components/form/utils/getRelationshipsForForm"; +import { buildRelationshipSchema } from "./getFormFieldsFromSchema.test"; + +describe("getRelationshipsForForm", () => { + it("returns an empty array if the provided relationships array is empty", () => { + // GIVEN + const relationships: never[] = []; + + // WHEN + const result = getRelationshipsForForm(relationships); + + // THEN + expect(result).toEqual([]); + }); + + it("returns a relationship if cardinality is one", () => { + // GIVEN + const relationships = [buildRelationshipSchema({ cardinality: "one" })]; + + // WHEN + const result = getRelationshipsForForm(relationships); + + // THEN + expect(result).toEqual([relationships[0]]); + }); + + it("returns a relationship of cardinality many if kind is Attribute or Parent", () => { + // GIVEN + const relationships = [ + buildRelationshipSchema({ cardinality: "many", kind: "Attribute" }), + buildRelationshipSchema({ cardinality: "many", kind: "Parent" }), + ]; + + // WHEN + const result = getRelationshipsForForm(relationships); + + // THEN + expect(result).toEqual(relationships); + }); + + it("should not return a relationship of cardinality many if kind is Generic/Component/Hierarchy", () => { + // GIVEN + const relationships = [ + buildRelationshipSchema({ cardinality: "many", kind: "Generic" }), + buildRelationshipSchema({ cardinality: "many", kind: "Component" }), + buildRelationshipSchema({ cardinality: "many", kind: "Hierarchy" }), + buildRelationshipSchema({ cardinality: "many", kind: "Group" }), + buildRelationshipSchema({ cardinality: "many", kind: "Profile" }), + ]; + + // WHEN + const result = getRelationshipsForForm(relationships); + + // THEN + expect(result).toEqual([]); + }); + + it("returns a relationship of cardinality many if it's mandatory field", () => { + // GIVEN + const relationships = [ + buildRelationshipSchema({ cardinality: "many", kind: "Attribute", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Parent", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Generic", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Component", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Hierarchy", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Group", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Profile", optional: false }), + ]; + + // WHEN + const result = getRelationshipsForForm(relationships); + + // THEN + expect(result).toEqual(relationships); + }); + + it("When update, returns a mandatory relationship of cardinality is many if kind is Attribute or Parent", () => { + // GIVEN + const relationships = [ + buildRelationshipSchema({ cardinality: "many", kind: "Attribute", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Parent", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Generic", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Component", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Hierarchy", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Group", optional: false }), + buildRelationshipSchema({ cardinality: "many", kind: "Profile", optional: false }), + ]; + const isUpdate = true; + + // WHEN + const result = getRelationshipsForForm(relationships, isUpdate); + + // THEN + expect(result).toEqual([relationships[0], relationships[1]]); + }); +});