From 714001c7df1d670df1c0928e8f1005fa6a5cbcef Mon Sep 17 00:00:00 2001 From: Tomas Engebretsen Date: Wed, 25 Sep 2024 14:58:18 +0200 Subject: [PATCH] fix: Generate unique pointer correctly (#13635) --- .../schema-model/src/lib/SchemaModel.test.ts | 33 +++++++++++++---- .../schema-model/src/lib/SchemaModel.ts | 8 ++--- .../schema-model/src/lib/pointerUtils.test.ts | 25 +++++++++++++ .../schema-model/src/lib/pointerUtils.ts | 20 ++++++++++- .../schema-model/test/uiSchemaMock.ts | 35 +++++++++++++++++++ 5 files changed, 110 insertions(+), 11 deletions(-) diff --git a/frontend/packages/schema-model/src/lib/SchemaModel.test.ts b/frontend/packages/schema-model/src/lib/SchemaModel.test.ts index e6eb4c4bcd8..3259b8387a4 100644 --- a/frontend/packages/schema-model/src/lib/SchemaModel.test.ts +++ b/frontend/packages/schema-model/src/lib/SchemaModel.test.ts @@ -3,6 +3,8 @@ import { allOfNodeChildMock, allOfNodeMock, arrayNodeMock, + combinationDefNodeChild1Mock, + combinationDefNodeMock, combinationNodeWithMultipleChildrenMock, defNodeMock, defNodeWithChildrenChildMock, @@ -16,6 +18,7 @@ import { parentNodeMock, referenceDefinitionMock, referenceNodeMock, + referenceToCombinationDefNodeMock, referenceToObjectNodeMock, requiredNodeMock, rootNodeMock, @@ -145,11 +148,10 @@ describe('SchemaModel', () => { }); describe('getSchemaPointerByUniquePointer', () => { - const uniqueGrandChildPointer = - '#/properties/referenceToParent/properties/child/properties/grandchild'; - const uniqueChildPointer = '#/properties/referenceToParent/properties/child'; - - it('Returns the schema pointer for a given unique pointer', () => { + it('Returns the schema pointer for a given unique pointer to an object', () => { + const uniqueGrandChildPointer = + '#/properties/referenceToParent/properties/child/properties/grandchild'; + const uniqueChildPointer = '#/properties/referenceToParent/properties/child'; expect(schemaModel.getSchemaPointerByUniquePointer(uniqueChildPointer)).toEqual( defNodeWithChildrenChildMock.schemaPointer, ); @@ -157,6 +159,13 @@ describe('SchemaModel', () => { defNodeWithChildrenGrandchildMock.schemaPointer, ); }); + + it('Returns the schema pointer for a given unique pointer to a combination', () => { + const uniquePointer = '#/properties/referenceToCombinationDef/oneOf/0'; + const expectedResult = combinationDefNodeChild1Mock.schemaPointer; + const result = schemaModel.getSchemaPointerByUniquePointer(uniquePointer); + expect(result).toEqual(expectedResult); + }); }); describe('getUniquePointer', () => { @@ -172,7 +181,7 @@ describe('SchemaModel', () => { ); }); - it('Returns a pointer reflecting the path to a given node in a reference', () => { + it('Returns a pointer reflecting the path to a given node in a reference to an object', () => { const expectedChildPointer = '#/properties/referenceToParent/properties/child'; const expectedGrandchildPointer = '#/properties/referenceToParent/properties/child/properties/grandchild'; @@ -190,6 +199,14 @@ describe('SchemaModel', () => { ), ).toEqual(expectedGrandchildPointer); }); + + it('Returns a pointer reflecting the path to a given node in a reference to a combination', () => { + const { schemaPointer } = combinationDefNodeChild1Mock; + const uniquePointerOfParent = referenceToCombinationDefNodeMock.schemaPointer; + const result = schemaModel.getUniquePointer(schemaPointer, uniquePointerOfParent); + const expectedResult = '#/properties/referenceToCombinationDef/oneOf/0'; + expect(result).toEqual(expectedResult); + }); }); describe('hasNode', () => { @@ -229,6 +246,7 @@ describe('SchemaModel', () => { unusedDefinitionMock, unusedDefinitionWithSameNameAsExistingObjectMock, referenceDefinitionMock, + combinationDefNodeMock, ]); }); }); @@ -244,6 +262,7 @@ describe('SchemaModel', () => { referenceToObjectNodeMock, nodeWithSameNameAsStringNodeMock, combinationNodeWithMultipleChildrenMock, + referenceToCombinationDefNodeMock, ]); }); }); @@ -264,6 +283,8 @@ describe('SchemaModel', () => { referenceDefinitionMock, nodeWithSameNameAsStringNodeMock, combinationNodeWithMultipleChildrenMock, + referenceToCombinationDefNodeMock, + combinationDefNodeMock, ]); }); }); diff --git a/frontend/packages/schema-model/src/lib/SchemaModel.ts b/frontend/packages/schema-model/src/lib/SchemaModel.ts index 99577ee6ef8..eabf91f7154 100644 --- a/frontend/packages/schema-model/src/lib/SchemaModel.ts +++ b/frontend/packages/schema-model/src/lib/SchemaModel.ts @@ -1,7 +1,6 @@ import { CombinationKind, FieldType, - Keyword, type NodePosition, type UiSchemaNode, type UiSchemaNodes, @@ -31,6 +30,7 @@ import { replaceStart } from 'app-shared/utils/stringUtils'; import { createDefinitionPointer, createPropertyPointer, + extractCategoryFromPointer, extractNameFromPointer, makePointerFromArray, } from './pointerUtils'; @@ -90,7 +90,7 @@ export class SchemaModel { const parentNodePointer = this.getParentSchemaPointerByUniquePointer(uniquePointer); return makePointerFromArray([ parentNodePointer, - Keyword.Properties, + extractCategoryFromPointer(uniquePointer), extractNameFromPointer(uniquePointer), ]); } @@ -109,8 +109,8 @@ export class SchemaModel { public getUniquePointer(schemaPointer: string, uniqueParentPointer?: string): string { if (!uniqueParentPointer || !isDefinitionPointer(schemaPointer)) return schemaPointer; - - return `${uniqueParentPointer}/properties/${extractNameFromPointer(schemaPointer)}`; + const category = extractCategoryFromPointer(schemaPointer); + return `${uniqueParentPointer}/${category}/${extractNameFromPointer(schemaPointer)}`; } public hasNode(schemaPointer: string): boolean { diff --git a/frontend/packages/schema-model/src/lib/pointerUtils.test.ts b/frontend/packages/schema-model/src/lib/pointerUtils.test.ts index 8e914196b5e..ade02d01acd 100644 --- a/frontend/packages/schema-model/src/lib/pointerUtils.test.ts +++ b/frontend/packages/schema-model/src/lib/pointerUtils.test.ts @@ -3,6 +3,7 @@ import { changeNameInPointer, createDefinitionPointer, createPropertyPointer, + extractCategoryFromPointer, extractNameFromPointer, makePointerFromArray, } from './pointerUtils'; @@ -13,6 +14,7 @@ import { simpleParentNodeMock, stringNodeMock, } from '../../test/uiSchemaMock'; +import { CombinationKind, Keyword } from '@altinn/schema-model/types'; describe('pointerUtils', () => { test('makePointerFromArray', () => { @@ -65,6 +67,29 @@ describe('pointerUtils', () => { }); }); + describe('extractCategoryFromPointer', () => { + it('Returns "properties" when the pointer is a property pointer', () => { + expect(extractCategoryFromPointer('#/properties/hello')).toBe(Keyword.Properties); + expect(extractCategoryFromPointer('#/anyOf/hello/properties/world')).toBe(Keyword.Properties); + expect(extractCategoryFromPointer('#/$defs/test/properties/hello')).toBe(Keyword.Properties); + }); + + it('Returns the combination kind when the pointer is a combination pointer', () => { + expect(extractCategoryFromPointer('#/allOf/0')).toBe(CombinationKind.AllOf); + expect(extractCategoryFromPointer('#/properties/test/anyOf/1')).toBe(CombinationKind.AnyOf); + expect(extractCategoryFromPointer('#/allOf/test/oneOf/2')).toBe(CombinationKind.OneOf); + expect(extractCategoryFromPointer('#/$defs/test/anyOf/3')).toBe(CombinationKind.AnyOf); + }); + + it('Returns "$defs" when the pointer is a definition pointer', () => { + expect(extractCategoryFromPointer('#/$defs/test')).toBe(Keyword.Definitions); + }); + + it('Returns undefined when the pointer is the root pointer', () => { + expect(extractCategoryFromPointer('#')).toBe(undefined); + }); + }); + describe('changeNameInPointer', () => { it('Changes the last part of the pointer', () => { expect(changeNameInPointer('#/$defs/hello', 'world')).toBe('#/$defs/world'); diff --git a/frontend/packages/schema-model/src/lib/pointerUtils.ts b/frontend/packages/schema-model/src/lib/pointerUtils.ts index 169e9c207f7..1ed0f09252b 100644 --- a/frontend/packages/schema-model/src/lib/pointerUtils.ts +++ b/frontend/packages/schema-model/src/lib/pointerUtils.ts @@ -1,5 +1,6 @@ import type { UiSchemaNode } from '../types'; -import { Keyword, ObjectKind } from '../types'; +import { CombinationKind, Keyword, ObjectKind } from '../types'; + import { ROOT_POINTER } from './constants'; import type { FieldNode } from '../types/FieldNode'; import type { CombinationNode } from '../types/CombinationNode'; @@ -45,6 +46,23 @@ export const extractNameFromPointer = (pointer: string): string => { return parts.pop(); }; +export const extractCategoryFromPointer = ( + pointer: string, +): Keyword.Properties | Keyword.Definitions | CombinationKind | undefined => { + const parts = pointer.split('/'); + const category = parts[parts.length - 2]; + switch (category) { + case Keyword.Properties: + case Keyword.Definitions: + case CombinationKind.AllOf: + case CombinationKind.AnyOf: + case CombinationKind.OneOf: + return category; + default: + return undefined; + } +}; + export const changeNameInPointer = (pointer: string, newName: string): string => { const parts = pointer.split('/'); parts.pop(); diff --git a/frontend/packages/schema-model/test/uiSchemaMock.ts b/frontend/packages/schema-model/test/uiSchemaMock.ts index c54458f22e1..c713adcc445 100644 --- a/frontend/packages/schema-model/test/uiSchemaMock.ts +++ b/frontend/packages/schema-model/test/uiSchemaMock.ts @@ -45,6 +45,11 @@ const combinationNodeChild1Pointer = '#/properties/combinationNodeWithMultipleCh const combinationNodeChild2Pointer = '#/properties/combinationNodeWithMultipleChildren/anyOf/1'; const combinationNodeChild3Pointer = '#/properties/combinationNodeWithMultipleChildren/anyOf/2'; +const combinationDefNodePointer = '#/$defs/combinationDef'; +const combinationDefNodeChild1Pointer = '#/$defs/combinationDef/oneOf/0'; +const combinationDefNodeChild2Pointer = '#/$defs/combinationDef/oneOf/1'; +const referenceToCombinationDefNodePointer = '#/properties/referenceToCombinationDef'; + export const nodeMockBase: UiSchemaNode = { objectKind: ObjectKind.Field, fieldType: FieldType.String, @@ -77,6 +82,8 @@ export const rootNodeMock: FieldNode = { referenceDefinitionPointer, nodeWithSameNameAsStringNodePointer, combinationNodeWithMultipleChildrenPointer, + referenceToCombinationDefNodePointer, + combinationDefNodePointer, ], }; @@ -274,6 +281,30 @@ export const combinationNodeChild3Mock: FieldNode = { title: 'Child 3', }; +export const combinationDefNodeMock: CombinationNode = { + ...nodeMockBase, + schemaPointer: combinationDefNodePointer, + objectKind: ObjectKind.Combination, + combinationType: CombinationKind.OneOf, + children: [combinationDefNodeChild1Pointer, combinationDefNodeChild2Pointer], +}; + +export const combinationDefNodeChild1Mock: FieldNode = { + ...nodeMockBase, + schemaPointer: combinationDefNodeChild1Pointer, +}; + +export const combinationDefNodeChild2Mock: FieldNode = { + ...nodeMockBase, + schemaPointer: combinationDefNodeChild2Pointer, +}; + +export const referenceToCombinationDefNodeMock: ReferenceNode = { + ...defaultReferenceNode, + schemaPointer: referenceToCombinationDefNodePointer, + reference: combinationDefNodePointer, +}; + export const uiSchemaMock: UiSchemaNodes = [ rootNodeMock, parentNodeMock, @@ -305,4 +336,8 @@ export const uiSchemaMock: UiSchemaNodes = [ combinationNodeChild1Mock, combinationNodeChild2Mock, combinationNodeChild3Mock, + combinationDefNodeMock, + combinationDefNodeChild1Mock, + combinationDefNodeChild2Mock, + referenceToCombinationDefNodeMock, ];