Skip to content

Commit

Permalink
[AAE-16303] Handle nested property data source in data table (#8895)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomgny committed Sep 8, 2023
1 parent a8db044 commit ce881b7
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DataColumn, FormFieldModel, FormFieldTypes, FormModel, LogService } from '@alfresco/adf-core';
import { DataColumn, FormFieldModel, FormFieldTypes, FormModel, LogService, VariableConfig } from '@alfresco/adf-core';
import { By } from '@angular/platform-browser';
import { DataTableWidgetComponent } from './data-table.widget';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
Expand All @@ -32,7 +32,9 @@ import {
mockJsonProcessVariables,
mockSchemaDefinition,
mockJsonResponseEuropeCountriesData,
mockJsonResponseFormVariable
mockJsonResponseFormVariable,
mockJsonNestedResponseFormVariable,
mockJsonNestedResponseEuropeCountriesData
} from '../../../mocks/data-table-widget.mock';

describe('DataTableWidgetComponent', () => {
Expand All @@ -45,7 +47,7 @@ describe('DataTableWidgetComponent', () => {
const errorIcon: string = 'error_outline';

const getDataVariable = (
variableName: string,
variableConfig: VariableConfig,
schemaDefinition: DataColumn[],
processVariables?: TaskVariableCloud[],
variables?: TaskVariableCloud[]
Expand All @@ -56,11 +58,19 @@ describe('DataTableWidgetComponent', () => {
type: FormFieldTypes.DATA_TABLE,
optionType: 'variable',
schemaDefinition,
variableConfig: {
variableName
}
variableConfig
});

const mockVariableConfig: VariableConfig = {
variableName: 'json-form-variable'
};

const checkDataTableErrorMessage = () => {
const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message'));

expect(failedErrorMsgElement.nativeElement.textContent.trim()).toBe(errorIcon.concat('FORM.FIELD.DATA_TABLE_LOAD_FAILED'));
};

beforeEach(() => {
TestBed.configureTestingModule({
imports: [
Expand Down Expand Up @@ -101,7 +111,7 @@ describe('DataTableWidgetComponent', () => {
});

it('should properly initialize column schema', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonFormVariable);
widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariable);
fixture.detectChanges();

widget.dataSource.getColumns().forEach((column, index) =>
Expand All @@ -110,7 +120,7 @@ describe('DataTableWidgetComponent', () => {
});

it('should properly initialize data source with priority on the field value if process and form variables are provided', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, mockJsonProcessVariables, mockJsonFormVariable);
widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, mockJsonProcessVariables, mockJsonFormVariable);
widget.field.value = mockAmericaCountriesData;
fixture.detectChanges();

Expand All @@ -121,7 +131,7 @@ describe('DataTableWidgetComponent', () => {
});

it('should properly initialize data source based on field value', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], []);
widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], []);
widget.field.value = mockAmericaCountriesData;
fixture.detectChanges();

Expand All @@ -131,8 +141,8 @@ describe('DataTableWidgetComponent', () => {
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
});

it('should properly initialize json response data source based on field value', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], []);
it('should properly initialize default json response data source based on field value if path is NOT provided', () => {
widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], []);
widget.field.value = mockJsonResponseEuropeCountriesData;
fixture.detectChanges();

Expand All @@ -142,8 +152,29 @@ describe('DataTableWidgetComponent', () => {
expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
});

it('should properly initialize json response data source based on variable', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonResponseFormVariable);
it('should properly initialize default json response data source based on variable if path is NOT provided', () => {
widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonResponseFormVariable);
fixture.detectChanges();

const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
expectedData.getRows().forEach(row => row.cssClass = '');

expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
});

it('should properly initialize json response data source based on field value if path is provided', () => {
widget.field = getDataVariable({ ...mockVariableConfig, optionsPath: 'response.my-data' }, mockSchemaDefinition, [], []);
widget.field.value = mockJsonNestedResponseEuropeCountriesData;
fixture.detectChanges();

const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
expectedData.getRows().forEach(row => row.cssClass = '');

expect(widget.dataSource.getRows()).toEqual(expectedData.getRows());
});

it('should properly initialize json response data source based on variable if path is provided', () => {
widget.field = getDataVariable({ ...mockVariableConfig, optionsPath: 'response.my-data' }, mockSchemaDefinition, [], mockJsonNestedResponseFormVariable);
fixture.detectChanges();

const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
Expand All @@ -153,7 +184,7 @@ describe('DataTableWidgetComponent', () => {
});

it('should properly initialize data source based on form variable', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonFormVariable);
widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariable);
fixture.detectChanges();

const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
Expand All @@ -163,7 +194,7 @@ describe('DataTableWidgetComponent', () => {
});

it('should properly initialize data source based on process variable', () => {
widget.field = getDataVariable('json-variable', mockSchemaDefinition, mockJsonProcessVariables);
widget.field = getDataVariable({ variableName: 'json-variable' }, mockSchemaDefinition, mockJsonProcessVariables);
fixture.detectChanges();

const expectedData = new WidgetDataTableAdapter(mockEuropeCountriesData, mockSchemaDefinition);
Expand All @@ -173,7 +204,7 @@ describe('DataTableWidgetComponent', () => {
});

it('should NOT display error if form is in preview state', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
spyOn(formCloudService, 'getPreviewState').and.returnValue(true);
fixture.detectChanges();

Expand All @@ -185,7 +216,7 @@ describe('DataTableWidgetComponent', () => {
});

it('should NOT display data table with data source if form is in preview state', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonFormVariable);
widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariable);
spyOn(formCloudService, 'getPreviewState').and.returnValue(true);
fixture.detectChanges();

Expand All @@ -196,36 +227,48 @@ describe('DataTableWidgetComponent', () => {
expect(dataTable).toBeNull();
});

it('should be able to display and log error if data source is not linked to every column', () => {
widget.field = getDataVariable('json-form-variable', mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
it('should display and log error if data source is not linked to every column', () => {
widget.field = getDataVariable(mockVariableConfig, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
fixture.detectChanges();

const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message'));

expect(failedErrorMsgElement.nativeElement.textContent.trim()).toBe(errorIcon.concat('FORM.FIELD.DATA_TABLE_LOAD_FAILED'));
checkDataTableErrorMessage();
expect(logServiceSpy).toHaveBeenCalledWith('Data source has corrupted model or structure');
expect(widget.dataSource.getRows()).toEqual([]);
});

it('should be able to display and log error if data source has invalid column structure', () => {
widget.field = getDataVariable('json-form-variable', mockInvalidSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
it('should display and log error if data source has invalid column structure', () => {
widget.field = getDataVariable(mockVariableConfig, mockInvalidSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
fixture.detectChanges();

const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message'));

expect(failedErrorMsgElement.nativeElement.textContent.trim()).toBe(errorIcon.concat('FORM.FIELD.DATA_TABLE_LOAD_FAILED'));
checkDataTableErrorMessage();
expect(logServiceSpy).toHaveBeenCalledWith('Data source has corrupted model or structure');
expect(widget.dataSource.getRows()).toEqual([]);
});

it('should be able to display and log error if data source is not found', () => {
widget.field = getDataVariable('not-found-data-source', mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
it('should display and log error if data source is not found', () => {
widget.field = getDataVariable({ variableName: 'not-found-data-source' }, mockSchemaDefinition, [], mockJsonFormVariableWithIncorrectData);
fixture.detectChanges();

const failedErrorMsgElement = fixture.debugElement.query(By.css('.adf-data-table-widget-failed-message'));
checkDataTableErrorMessage();
expect(logServiceSpy).toHaveBeenCalledWith('Data source not found or it is not an array');
expect(widget.dataSource).toBeUndefined();
});

expect(failedErrorMsgElement.nativeElement.textContent.trim()).toBe(errorIcon.concat('FORM.FIELD.DATA_TABLE_LOAD_FAILED'));
expect(logServiceSpy).toHaveBeenCalledWith('Data source not found');
it('should display and log error if path is incorrect', () => {
widget.field = getDataVariable({ ...mockVariableConfig, optionsPath: 'wrong.path' }, mockSchemaDefinition, mockJsonNestedResponseFormVariable, []);
fixture.detectChanges();

checkDataTableErrorMessage();
expect(logServiceSpy).toHaveBeenCalledWith('Data source not found or it is not an array');
expect(widget.dataSource).toBeUndefined();
});

it('should display and log error if provided data by path is not an array', () => {
widget.field = getDataVariable({ ...mockVariableConfig, optionsPath: 'response.no-array' }, mockSchemaDefinition, mockJsonNestedResponseFormVariable, []);
fixture.detectChanges();

checkDataTableErrorMessage();
expect(logServiceSpy).toHaveBeenCalledWith('Data source not found or it is not an array');
expect(widget.dataSource).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit
}

private initDataTable(): void {
if (this.rowsData) {
if (this.rowsData?.length) {
this.dataSource = new WidgetDataTableAdapter(this.rowsData, this.columnsSchema);

if (this.dataSource.isDataSourceValid()) {
Expand All @@ -99,16 +99,18 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit
this.handleError('Data source has corrupted model or structure');
}
} else {
this.handleError('Data source not found');
this.handleError('Data source not found or it is not an array');
}
}

private getRowsData(): void {
const optionsPath = this.field?.variableConfig?.optionsPath ?? this.defaultResponseProperty;
const fieldValue = this.field?.value;
const rowsData = fieldValue ? fieldValue : this.getDataFromVariable();
const rowsData = fieldValue || this.getDataFromVariable();

if (rowsData) {
this.rowsData = rowsData[this.defaultResponseProperty] || rowsData as DataRow[];
const dataFromPath = this.getOptionsFromPath(rowsData, optionsPath);
this.rowsData = dataFromPath?.length ? dataFromPath : rowsData as DataRow[];
}
}

Expand All @@ -122,6 +124,23 @@ export class DataTableWidgetComponent extends WidgetComponent implements OnInit
return processVariableDropdownOptions ?? formVariableDropdownOptions;
}

private getOptionsFromPath(data: any, path: string): DataRow[] {
const properties = path.split('.');
const currentProperty = properties.shift();

if (!data.hasOwnProperty(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 {
return variables?.find((variable: TaskVariableCloud) => variable?.name === `variables.${variableName}` || variable?.name === variableName)?.value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,21 @@ export const mockEuropeCountriesData = [
];

export const mockJsonResponseEuropeCountriesData = {
data: [
{
id: 'PL',
name: 'Poland'
},
{
id: 'IT',
name: 'Italy'
},
{
id: 'UK',
name: 'United Kingdom'
}
]
data: mockEuropeCountriesData
};

export const mockJsonNestedResponseEuropeCountriesData = {
response: {
empty: [],
'my-data': mockEuropeCountriesData,
data: [
{
id: 'HR',
name: 'Croatia'
}
],
'no-array': {}
}
};

export const mockAmericaCountriesData = [
Expand Down Expand Up @@ -137,6 +138,10 @@ export const mockJsonResponseFormVariable = [
new TaskVariableCloud({ name: 'json-form-variable', value: mockJsonResponseEuropeCountriesData, type: 'json', id: 'fake-id-1' })
];

export const mockJsonNestedResponseFormVariable = [
new TaskVariableCloud({ name: 'json-form-variable', value: mockJsonNestedResponseEuropeCountriesData, type: 'json', id: 'fake-id-1' })
];

export const mockJsonProcessVariables = [
new TaskVariableCloud({ name: 'variables.json-variable', value: mockEuropeCountriesData, type: 'json', id: 'fake-id-1' }),
new TaskVariableCloud({ name: 'variables.different-variable', value: 'fake-value', type: 'json', id: 'fake-id-2' })
Expand Down

0 comments on commit ce881b7

Please sign in to comment.