mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
AAE-21946 Support JSON paths with non-standard characters for data ta… (#9571)
* AAE-21946 Support JSON paths with non-standard characters for data table widget * add unit test * move path parse to extern helper class
This commit is contained in:
@@ -73,6 +73,11 @@ describe('DataTableWidgetComponent', () => {
|
|||||||
|
|
||||||
const getPreview = () => fixture.nativeElement.querySelector('[data-automation-id="adf-data-table-widget-preview"]');
|
const getPreview = () => fixture.nativeElement.querySelector('[data-automation-id="adf-data-table-widget-preview"]');
|
||||||
|
|
||||||
|
const assertDataRows = (expectedData: WidgetDataTableAdapter) => {
|
||||||
|
expectedData.getRows().forEach((row) => (row.cssClass = ''));
|
||||||
|
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [ProcessServiceCloudTestingModule]
|
imports: [ProcessServiceCloudTestingModule]
|
||||||
@@ -123,9 +128,7 @@ describe('DataTableWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const expectedData = new WidgetDataTableAdapter(mockAmericaCountriesData, mockSchemaDefinition);
|
const expectedData = new WidgetDataTableAdapter(mockAmericaCountriesData, mockSchemaDefinition);
|
||||||
expectedData.getRows().forEach((row) => (row.cssClass = ''));
|
assertDataRows(expectedData);
|
||||||
|
|
||||||
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly initialize data source based on field value', () => {
|
it('should properly initialize data source based on field value', () => {
|
||||||
@@ -134,9 +137,7 @@ describe('DataTableWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const expectedData = new WidgetDataTableAdapter(mockAmericaCountriesData, mockSchemaDefinition);
|
const expectedData = new WidgetDataTableAdapter(mockAmericaCountriesData, mockSchemaDefinition);
|
||||||
expectedData.getRows().forEach((row) => (row.cssClass = ''));
|
assertDataRows(expectedData);
|
||||||
|
|
||||||
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly initialize default json response data source based on field value if path is NOT provided', () => {
|
it('should properly initialize default json response data source based on field value if path is NOT provided', () => {
|
||||||
@@ -145,9 +146,7 @@ describe('DataTableWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
||||||
expectedData.getRows().forEach((row) => (row.cssClass = ''));
|
assertDataRows(expectedData);
|
||||||
|
|
||||||
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly initialize default json response data source based on variable if path is NOT provided', () => {
|
it('should properly initialize default json response data source based on variable if path is NOT provided', () => {
|
||||||
@@ -155,9 +154,7 @@ describe('DataTableWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
||||||
expectedData.getRows().forEach((row) => (row.cssClass = ''));
|
assertDataRows(expectedData);
|
||||||
|
|
||||||
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly initialize json response data source based on field value if path is provided', () => {
|
it('should properly initialize json response data source based on field value if path is provided', () => {
|
||||||
@@ -166,9 +163,7 @@ describe('DataTableWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
||||||
expectedData.getRows().forEach((row) => (row.cssClass = ''));
|
assertDataRows(expectedData);
|
||||||
|
|
||||||
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly initialize json response data source based on variable if path is provided', () => {
|
it('should properly initialize json response data source based on variable if path is provided', () => {
|
||||||
@@ -181,9 +176,7 @@ describe('DataTableWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
||||||
expectedData.getRows().forEach((row) => (row.cssClass = ''));
|
assertDataRows(expectedData);
|
||||||
|
|
||||||
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly initialize data source based on form variable', () => {
|
it('should properly initialize data source based on form variable', () => {
|
||||||
@@ -191,9 +184,7 @@ describe('DataTableWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
||||||
expectedData.getRows().forEach((row) => (row.cssClass = ''));
|
assertDataRows(expectedData);
|
||||||
|
|
||||||
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly initialize data source based on process variable', () => {
|
it('should properly initialize data source based on process variable', () => {
|
||||||
@@ -201,9 +192,7 @@ describe('DataTableWidgetComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
|
||||||
expectedData.getRows().forEach((row) => (row.cssClass = ''));
|
assertDataRows(expectedData);
|
||||||
|
|
||||||
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should NOT display error if form is in preview state', () => {
|
it('should NOT display error if form is in preview state', () => {
|
||||||
|
@@ -18,29 +18,17 @@
|
|||||||
/* eslint-disable @angular-eslint/component-selector */
|
/* eslint-disable @angular-eslint/component-selector */
|
||||||
|
|
||||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
import {
|
import { WidgetComponent, FormService, DataTableModule, LogService, FormBaseModule, DataRow, DataColumn } from '@alfresco/adf-core';
|
||||||
WidgetComponent,
|
|
||||||
FormService,
|
|
||||||
DataTableModule,
|
|
||||||
LogService,
|
|
||||||
FormBaseModule,
|
|
||||||
DataRow,
|
|
||||||
DataColumn
|
|
||||||
} from '@alfresco/adf-core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { FormCloudService } from '../../../services/form-cloud.service';
|
import { FormCloudService } from '../../../services/form-cloud.service';
|
||||||
import { TaskVariableCloud } from '../../../models/task-variable-cloud.model';
|
import { TaskVariableCloud } from '../../../models/task-variable-cloud.model';
|
||||||
import { WidgetDataTableAdapter } from './data-table-adapter.widget';
|
import { WidgetDataTableAdapter } from './data-table-adapter.widget';
|
||||||
|
import { DataTablePathParserHelper } from './helpers/data-table-path-parser.helper';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [CommonModule, TranslateModule, DataTableModule, FormBaseModule],
|
||||||
CommonModule,
|
|
||||||
TranslateModule,
|
|
||||||
DataTableModule,
|
|
||||||
FormBaseModule
|
|
||||||
],
|
|
||||||
selector: 'data-table',
|
selector: 'data-table',
|
||||||
templateUrl: './data-table.widget.html',
|
templateUrl: './data-table.widget.html',
|
||||||
styleUrls: ['./data-table.widget.scss'],
|
styleUrls: ['./data-table.widget.scss'],
|
||||||
@@ -58,7 +46,6 @@ import { WidgetDataTableAdapter } from './data-table-adapter.widget';
|
|||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class DataTableWidgetComponent extends WidgetComponent implements OnInit {
|
export class DataTableWidgetComponent extends WidgetComponent implements OnInit {
|
||||||
|
|
||||||
dataSource: WidgetDataTableAdapter;
|
dataSource: WidgetDataTableAdapter;
|
||||||
dataTableLoadFailed = false;
|
dataTableLoadFailed = false;
|
||||||
previewState = false;
|
previewState = false;
|
||||||
@@ -67,12 +54,9 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit
|
|||||||
private columnsSchema: DataColumn[];
|
private columnsSchema: DataColumn[];
|
||||||
private variableName: string;
|
private variableName: string;
|
||||||
private defaultResponseProperty = 'data';
|
private defaultResponseProperty = 'data';
|
||||||
|
private pathParserHelper = new DataTablePathParserHelper();
|
||||||
|
|
||||||
constructor(
|
constructor(public formService: FormService, private formCloudService: FormCloudService, private logService: LogService) {
|
||||||
public formService: FormService,
|
|
||||||
private formCloudService: FormCloudService,
|
|
||||||
private logService: LogService
|
|
||||||
) {
|
|
||||||
super(formService);
|
super(formService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,8 +93,8 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit
|
|||||||
const rowsData = fieldValue || this.getDataFromVariable();
|
const rowsData = fieldValue || this.getDataFromVariable();
|
||||||
|
|
||||||
if (rowsData) {
|
if (rowsData) {
|
||||||
const dataFromPath = this.getOptionsFromPath(rowsData, optionsPath);
|
const dataFromPath = this.pathParserHelper.retrieveDataFromPath(rowsData, optionsPath);
|
||||||
this.rowsData = dataFromPath?.length ? dataFromPath : rowsData as DataRow[];
|
this.rowsData = (dataFromPath?.length ? dataFromPath : rowsData) as DataRow[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,25 +108,9 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit
|
|||||||
return processVariableDropdownOptions ?? formVariableDropdownOptions;
|
return processVariableDropdownOptions ?? formVariableDropdownOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOptionsFromPath(data: any, path: string): DataRow[] {
|
|
||||||
const properties = path.split('.');
|
|
||||||
const currentProperty = properties.shift();
|
|
||||||
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(data, currentProperty)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const nestedData = data[currentProperty];
|
|
||||||
|
|
||||||
if (Array.isArray(nestedData)) {
|
|
||||||
return nestedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.getOptionsFromPath(nestedData, properties.join('.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private getVariableValueByName(variables: TaskVariableCloud[], variableName: string): any {
|
private getVariableValueByName(variables: TaskVariableCloud[], variableName: string): any {
|
||||||
return variables?.find((variable: TaskVariableCloud) => variable?.name === `variables.${variableName}` || variable?.name === variableName)?.value;
|
return variables?.find((variable: TaskVariableCloud) => variable?.name === `variables.${variableName}` || variable?.name === variableName)
|
||||||
|
?.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setPreviewState(): void {
|
private setPreviewState(): void {
|
||||||
|
@@ -0,0 +1,88 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DataTablePathParserHelper } from './data-table-path-parser.helper';
|
||||||
|
import {
|
||||||
|
mockEuropeCountriesData,
|
||||||
|
mockJsonNestedResponseEuropeCountriesDataWithSeparatorInPropertyName,
|
||||||
|
mockJsonNestedResponseEuropeCountriesDataWithMultipleSpecialCharacters,
|
||||||
|
mockJsonNestedResponseEuropeCountriesData
|
||||||
|
} from '../../../../mocks/data-table-widget.mock';
|
||||||
|
|
||||||
|
describe('DataTablePathParserHelper', () => {
|
||||||
|
let helper: DataTablePathParserHelper;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
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']);
|
||||||
|
});
|
||||||
|
|
||||||
|
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']);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,50 @@
|
|||||||
|
/*!
|
||||||
|
* @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 class DataTablePathParserHelper {
|
||||||
|
private readonly splitPathRegEx = /\.(?![^[]*\])/g;
|
||||||
|
private readonly removeSquareBracketsRegEx = /^\[(.*)\]$/;
|
||||||
|
|
||||||
|
retrieveDataFromPath(data: any, path: string): any[] {
|
||||||
|
const properties = this.splitPathIntoProperties(path);
|
||||||
|
const currentProperty = this.removeSquareBracketsFromProperty(properties.shift());
|
||||||
|
|
||||||
|
if (!this.isPropertyExistsInData(data, currentProperty)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const nestedData = data[currentProperty];
|
||||||
|
|
||||||
|
if (Array.isArray(nestedData)) {
|
||||||
|
return nestedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.retrieveDataFromPath(nestedData, properties.join('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private splitPathIntoProperties(path: string): string[] {
|
||||||
|
return path.split(this.splitPathRegEx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private removeSquareBracketsFromProperty(property: string): string {
|
||||||
|
return property.replace(this.removeSquareBracketsRegEx, '$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
private isPropertyExistsInData(data: any, property: string): boolean {
|
||||||
|
return Object.prototype.hasOwnProperty.call(data, property);
|
||||||
|
}
|
||||||
|
}
|
@@ -85,6 +85,28 @@ 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 = [
|
export const mockAmericaCountriesData = [
|
||||||
{
|
{
|
||||||
id: 'CA',
|
id: 'CA',
|
||||||
|
Reference in New Issue
Block a user