From a03936715ad1d18d2f25403e0de0d119940538d3 Mon Sep 17 00:00:00 2001 From: Tomasz Gnyp <49343696+tomgny@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:09:24 +0200 Subject: [PATCH] AAE-22222 Fix JSON path doesn't work for square bracekts (#9611) * AAE-22222 Fix JSON path doesn't work for square bracekts * remove comment * remove test description duplications * remove duplications --- .../data-table-adapter.widget.spec.ts | 2 +- .../data-table/data-table.widget.spec.ts | 2 +- .../data-table-path-parser.helper.spec.ts | 143 ++++++++++-------- .../helpers/data-table-path-parser.helper.ts | 42 ++++- .../data-table-path-parser.helper.mock.ts | 45 ++++++ .../mocks/data-table-widget.mock.ts | 24 +-- 6 files changed, 171 insertions(+), 87 deletions(-) create mode 100644 lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-path-parser.helper.mock.ts rename lib/process-services-cloud/src/lib/form/{ => components/widgets/data-table}/mocks/data-table-widget.mock.ts (84%) diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table-adapter.widget.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table-adapter.widget.spec.ts index 460e56a424d..04d9dd2e553 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table-adapter.widget.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table-adapter.widget.spec.ts @@ -21,7 +21,7 @@ import { mockCountriesIncorrectData, mockInvalidSchemaDefinition, mockSchemaDefinition -} from '../../../mocks/data-table-widget.mock'; +} from './mocks/data-table-widget.mock'; import { ObjectDataRow } from '@alfresco/adf-core'; describe('WidgetDataTableAdapter', () => { diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.spec.ts index 79ba05d47bf..2bef6e98af5 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/data-table.widget.spec.ts @@ -35,7 +35,7 @@ import { mockJsonResponseFormVariable, mockJsonNestedResponseFormVariable, mockJsonNestedResponseEuropeCountriesData -} from '../../../mocks/data-table-widget.mock'; +} from './mocks/data-table-widget.mock'; describe('DataTableWidgetComponent', () => { let widget: DataTableWidgetComponent; diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.spec.ts index 46f6816eaf4..b506d33389f 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.spec.ts @@ -16,12 +16,7 @@ */ import { DataTablePathParserHelper } from './data-table-path-parser.helper'; -import { - mockEuropeCountriesData, - mockJsonNestedResponseEuropeCountriesDataWithSeparatorInPropertyName, - mockJsonNestedResponseEuropeCountriesDataWithMultipleSpecialCharacters, - mockJsonNestedResponseEuropeCountriesData -} from '../../../../mocks/data-table-widget.mock'; +import { mockResponseResultData, mockResultData } from '../mocks/data-table-path-parser.helper.mock'; describe('DataTablePathParserHelper', () => { let helper: DataTablePathParserHelper; @@ -30,59 +25,89 @@ describe('DataTablePathParserHelper', () => { helper = new DataTablePathParserHelper(); }); - it('should return the correct data for path with separator in nested brackets', () => { - const data = mockJsonNestedResponseEuropeCountriesDataWithSeparatorInPropertyName; - const path = 'response.[my.data].[country[data].country]'; - const result = helper.retrieveDataFromPath(data, path); - expect(result).toEqual(mockEuropeCountriesData); - }); - - it('should return the correct data for path with special characters except separator (.) in brackets', () => { - const data = mockJsonNestedResponseEuropeCountriesDataWithMultipleSpecialCharacters; - const path = 'response.[xyz:abc,xyz-abc,xyz_abc,abc+xyz]'; - const result = helper.retrieveDataFromPath(data, path); - expect(result).toEqual(mockEuropeCountriesData); - }); - - it('should return the correct data for path with special characters except separator (.) without brackets', () => { - const data = mockJsonNestedResponseEuropeCountriesDataWithMultipleSpecialCharacters; - const path = 'response.xyz:abc,xyz-abc,xyz_abc,abc+xyz'; - const result = helper.retrieveDataFromPath(data, path); - expect(result).toEqual(mockEuropeCountriesData); - }); - - it('should return the correct data for path without separator in brackets', () => { - const data = mockJsonNestedResponseEuropeCountriesData; - const path = '[response].[my-data]'; - const result = helper.retrieveDataFromPath(data, path); - expect(result).toEqual(mockEuropeCountriesData); - }); - - it('should return an empty array if the path does not exist in the data', () => { - const data = {}; - const path = 'nonexistent.path'; - const result = helper.retrieveDataFromPath(data, path); - expect(result).toEqual([]); - }); - - it('should return the correct data if the path is nested', () => { - const data = { level1: { level2: { level3: { level4: ['parrot', 'fish'] } } } }; - const path = 'level1.level2.level3.level4'; - const result = helper.retrieveDataFromPath(data, path); - expect(result).toEqual(['parrot', 'fish']); - }); - - it('should return the correct data if the path is NOT nested', () => { - const data = { pets: ['cat', 'dog'] }; - const path = 'pets'; - const result = helper.retrieveDataFromPath(data, path); - expect(result).toEqual(['cat', 'dog']); - }); + describe('should return the correct data for path', () => { + const testCases = [ + { + description: 'not existent', + data: {}, + path: 'nonexistent.path', + expected: [] + }, + { + description: 'nested', + data: { level1: { level2: { level3: { level4: ['parrot', 'fish'] } } } }, + propertyName: 'level4', + path: 'level1.level2.level3.level4', + expected: ['parrot', 'fish'] + }, + { + description: 'NOT nested', + data: { pets: ['cat', 'dog'] }, + propertyName: 'pets', + path: 'pets', + expected: ['cat', 'dog'] + }, + { + description: 'NOT nested with separator (.) in property name', + data: { 'my.pets': ['cat', 'dog'] }, + propertyName: 'my.pets', + path: '[my.pets]', + expected: ['cat', 'dog'] + }, + { + description: 'with nested brackets followed by an additional part of property name', + propertyName: 'file.file[data]file', + path: 'response.[file.file[data]file]' + }, + { + description: 'with nested brackets', + propertyName: 'file.file[data]', + path: 'response.[file.file[data]]' + }, + { + description: 'with separator before nested brackets in property name', + propertyName: 'file.[data]file', + path: 'response.[file.[data]file]' + }, + { + description: 'with separator before and no separator after nested brackets in property name', + propertyName: 'file.[data]', + path: 'response.[file.[data]]' + }, + { + description: 'with separator after nested brackets', + propertyName: 'file[data].file', + path: 'response.[file[data].file]' + }, + { + description: 'with multiple brackets in property name', + propertyName: 'file.file[data]file[data]', + path: 'response.[file.file[data]file[data]]' + }, + { + description: 'with special characters except separator (.) in brackets', + propertyName: 'xyz:abc,xyz-abc,xyz_abc,abc+xyz', + path: 'response.[xyz:abc,xyz-abc,xyz_abc,abc+xyz]' + }, + { + description: 'with special characters except separator (.) without brackets', + propertyName: 'xyz:abc,xyz-abc,xyz_abc,abc+xyz', + path: 'response.xyz:abc,xyz-abc,xyz_abc,abc+xyz' + }, + { + description: 'without separator in brackets', + propertyName: 'my-data', + path: '[response].[my-data]' + } + ]; - it('should return the correct data if the path is NOT nested with separator (.) in property name', () => { - const data = { 'my.pets': ['cat', 'dog'] }; - const path = '[my.pets]'; - const result = helper.retrieveDataFromPath(data, path); - expect(result).toEqual(['cat', 'dog']); + testCases.forEach((testCase) => { + it(testCase.description, () => { + const data = testCase.data ?? mockResponseResultData(testCase.propertyName); + const result = helper.retrieveDataFromPath(data, testCase.path); + const expectedResult = testCase.expected ?? mockResultData; + expect(result).toEqual(expectedResult); + }); + }); }); }); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.ts index 4a945c41137..5b5f5109279 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/helpers/data-table-path-parser.helper.ts @@ -16,7 +16,6 @@ */ export class DataTablePathParserHelper { - private readonly splitPathRegEx = /\.(?![^[]*\])/g; private readonly removeSquareBracketsRegEx = /^\[(.*)\]$/; retrieveDataFromPath(data: any, path: string): any[] { @@ -37,11 +36,48 @@ export class DataTablePathParserHelper { } private splitPathIntoProperties(path: string): string[] { - return path.split(this.splitPathRegEx); + const properties: string[] = []; + const separator = '.'; + const openBracket = '['; + const closeBracket = ']'; + + let currentPropertyBuffer = ''; + let bracketCount = 0; + const isPropertySeparatorOutsideBrackets = () => bracketCount === 0; + + for (const char of path) { + switch (char) { + case separator: + if (isPropertySeparatorOutsideBrackets()) { + properties.push(currentPropertyBuffer); + currentPropertyBuffer = ''; + } else { + currentPropertyBuffer += char; + } + break; + case openBracket: + bracketCount++; + currentPropertyBuffer += char; + break; + case closeBracket: + bracketCount--; + currentPropertyBuffer += char; + break; + default: + currentPropertyBuffer += char; + break; + } + } + + if (currentPropertyBuffer) { + properties.push(currentPropertyBuffer); + } + + return properties; } private removeSquareBracketsFromProperty(property: string): string { - return property.replace(this.removeSquareBracketsRegEx, '$1'); + return property?.replace(this.removeSquareBracketsRegEx, '$1'); } private isPropertyExistsInData(data: any, property: string): boolean { diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-path-parser.helper.mock.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-path-parser.helper.mock.ts new file mode 100644 index 00000000000..dce14d0dadf --- /dev/null +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-path-parser.helper.mock.ts @@ -0,0 +1,45 @@ +/*! + * @license + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const mockResultData = [ + { + id: '1', + decimal: '1.0', + name: 'test1', + date: '1-12-2023', + amount: '0.12', + data: '{ "result": "positive" }', + trust: 'true', + image: 'check_circle' + }, + { + id: '2', + decimal: '2.2', + name: 'test2', + date: '2-13-2023', + amount: '2.2', + data: '{ "result": "negative" }', + trust: 'true', + image: 'bookmark' + } +]; + +export const mockResponseResultData = (propertyName: string) => ({ + response: { + [propertyName]: mockResultData + } +}); diff --git a/lib/process-services-cloud/src/lib/form/mocks/data-table-widget.mock.ts b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-widget.mock.ts similarity index 84% rename from lib/process-services-cloud/src/lib/form/mocks/data-table-widget.mock.ts rename to lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-widget.mock.ts index bc0f80a4136..edfc53dba2e 100644 --- a/lib/process-services-cloud/src/lib/form/mocks/data-table-widget.mock.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/data-table/mocks/data-table-widget.mock.ts @@ -16,7 +16,7 @@ */ import { DataColumn } from '@alfresco/adf-core'; -import { TaskVariableCloud } from '../models/task-variable-cloud.model'; +import { TaskVariableCloud } from '../../../../models/task-variable-cloud.model'; export const mockSchemaDefinition: DataColumn[] = [ { @@ -85,28 +85,6 @@ export const mockJsonNestedResponseEuropeCountriesData = { } }; -export const mockJsonNestedResponseEuropeCountriesDataWithSeparatorInPropertyName = { - response: { - empty: [], - 'my.data': { - 'country[data].country': mockEuropeCountriesData - }, - data: [ - { - id: 'HR', - name: 'Croatia' - } - ], - 'no-array': {} - } -}; - -export const mockJsonNestedResponseEuropeCountriesDataWithMultipleSpecialCharacters = { - response: { - 'xyz:abc,xyz-abc,xyz_abc,abc+xyz': mockEuropeCountriesData - } -}; - export const mockAmericaCountriesData = [ { id: 'CA',