Skip to content

Commit

Permalink
AAE-21945 Support a nested object which is within an array in the JSO… (
Browse files Browse the repository at this point in the history
#9635)

* AAE-21945 Support a nested object which is within an array in the JSON datasource

* improve data table widget mocks

* improve object datatable adapter unit tests

* improve mocks

* modify data table widget adapter instead of core object data table adapter

* revert not needed changes

* improve method name

* remove automatic lint changes
  • Loading branch information
tomgny authored May 9, 2024
1 parent d59fbdd commit d0209d4
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
mockInvalidSchemaDefinition,
mockSchemaDefinition
} from './mocks/data-table-widget.mock';
import { ObjectDataRow } from '@alfresco/adf-core';
import { mockPersonDataFirstRow, mockPersonsData } from './mocks/data-table-adapter.mock';
import { DataColumn, ObjectDataColumn, ObjectDataRow } from '@alfresco/adf-core';

describe('WidgetDataTableAdapter', () => {
let widgetDataTableAdapter: WidgetDataTableAdapter;
Expand Down Expand Up @@ -64,4 +65,130 @@ describe('WidgetDataTableAdapter', () => {

expect(isValid).toBeTrue();
});

describe('should create proper rows and columns from schema and data with nested properties for', () => {
it('one column', () => {
const mockPersonSchema: DataColumn[] = [
{
type: 'text',
key: 'person.name',
title: 'Name'
}
];

const adapter = new WidgetDataTableAdapter(mockPersonsData, mockPersonSchema);
const rows = adapter.getRows();
const columns = adapter.getColumns();

const expectedFirstRow = new ObjectDataRow({
'person.name': 'John Doe'
});
const expectedSecondRow = new ObjectDataRow({
'person.name': 'Sam Smith'
});
const expectedColumns = [new ObjectDataColumn({ key: 'person.name', type: 'text', title: 'Name' })];

expect(rows.length).toBe(2);
expect(rows[0]).toEqual(expectedFirstRow);
expect(rows[1]).toEqual(expectedSecondRow);

expect(columns.length).toBe(1);
expect(columns).toEqual(expectedColumns);
});

it('one row', () => {
const mockPersonSchema: DataColumn[] = [
{
type: 'text',
key: 'name',
title: 'Name'
},
{
type: 'text',
key: 'personData.[address.[data]test].city',
title: 'City'
}
];

const adapter = new WidgetDataTableAdapter([mockPersonDataFirstRow], mockPersonSchema);
const rows = adapter.getRows();
const columns = adapter.getColumns();

const expectedFirstRow = new ObjectDataRow({
name: 'John Doe',
'personData.[address.[data]test].city': 'Springfield'
});
const expectedColumns = [
new ObjectDataColumn({ key: 'name', type: 'text', title: 'Name' }),
new ObjectDataColumn({ key: 'personData.[address.[data]test].city', type: 'text', title: 'City' })
];

expect(rows.length).toBe(1);
expect(rows[0]).toEqual(expectedFirstRow);

expect(columns.length).toBe(2);
expect(columns).toEqual(expectedColumns);
});

it('complex schema', () => {
const mockPersonSchema: DataColumn[] = [
{
type: 'text',
key: 'person.name',
title: 'Name'
},
{
type: 'text',
key: 'person.personData.[address.[data]test].city',
title: 'City'
},
{
type: 'text',
key: 'person.personData.[address.[data]test].street',
title: 'Street'
},
{
type: 'json',
key: 'person.phoneNumbers',
title: 'Phone numbers'
}
];

const adapter = new WidgetDataTableAdapter(mockPersonsData, mockPersonSchema);
const rows = adapter.getRows();
const columns = adapter.getColumns();

const expectedFirstRow = new ObjectDataRow({
'person.personData.[address.[data]test].city': 'Springfield',
'person.personData.[address.[data]test].street': '1234 Main St',
'person.name': 'John Doe',
'person.phoneNumbers': [
{ type: 'home', phoneNumber: '123-456-7890' },
{ type: 'work', phoneNumber: '098-765-4321' }
]
});
const expectedSecondRow = new ObjectDataRow({
'person.personData.[address.[data]test].city': 'Westlake',
'person.personData.[address.[data]test].street': '731 Second St',
'person.name': 'Sam Smith',
'person.phoneNumbers': [
{ type: 'home', phoneNumber: '123-456-7891' },
{ type: 'work', phoneNumber: '321-654-1987' }
]
});
const expectedColumns = [
new ObjectDataColumn({ key: 'person.name', type: 'text', title: 'Name' }),
new ObjectDataColumn({ key: 'person.personData.[address.[data]test].city', type: 'text', title: 'City' }),
new ObjectDataColumn({ key: 'person.personData.[address.[data]test].street', type: 'text', title: 'Street' }),
new ObjectDataColumn({ key: 'person.phoneNumbers', type: 'json', title: 'Phone numbers' })
];

expect(rows.length).toBe(2);
expect(rows[0]).toEqual(expectedFirstRow);
expect(rows[1]).toEqual(expectedSecondRow);

expect(columns.length).toBe(4);
expect(columns).toEqual(expectedColumns);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,118 @@
* limitations under the License.
*/

import {
ObjectDataTableAdapter,
DataColumn,
DataRow
} from '@alfresco/adf-core';
import { ObjectDataTableAdapter, DataColumn, DataRow, ObjectDataColumn, ObjectDataRow, DataTableAdapter, DataSorting } from '@alfresco/adf-core';
import { DataTablePathParserHelper } from './helpers/data-table-path-parser.helper';
import { Subject } from 'rxjs';

export class WidgetDataTableAdapter extends ObjectDataTableAdapter {
export class WidgetDataTableAdapter implements DataTableAdapter {
private adapter: ObjectDataTableAdapter;
private columnKeys: string[] = [];
private helper = new DataTablePathParserHelper();

private rows: DataRow[];
private columns: DataColumn[];
get selectedRow(): DataRow {
return this.adapter.selectedRow;
}

get rowsChanged(): Subject<Array<DataRow>> {
return this.adapter.rowsChanged;
}

constructor(data: any[], schema: DataColumn[]) {
this.adapter = new ObjectDataTableAdapter(data, schema);

this.createColumns(schema);
this.createRows(data);
}

private createColumns(schema: DataColumn[]): void {
if (schema?.length) {
this.adapter.setColumns(this.buildColumnsFromSchema(schema));
}
}

constructor(data?: any[], schema?: DataColumn[]) {
super(data, schema);
this.rows = super.getRows();
this.columns = super.getColumns();
private buildColumnsFromSchema(schema: DataColumn[]): ObjectDataColumn[] {
return schema.map((dataColumn) => {
this.columnKeys.push(dataColumn.key);

return new ObjectDataColumn(dataColumn);
});
}

private createRows(data: any[]): void {
if (data?.length) {
this.adapter.setRows(data.map((item) => this.buildDataRowFromItem(item)));
}
}

private buildDataRowFromItem(item: any): ObjectDataRow {
const rowData = {};
this.columnKeys.forEach((path, i) => {
const rowValue = this.extractPropertyValue(this.helper.splitPathIntoProperties(path), item);

if (rowValue) {
rowData[this.columnKeys[i]] = rowValue;
}
});

return new ObjectDataRow(rowData);
}

private extractPropertyValue(properties: string[], item: any): string {
return properties.reduce((acc, property) => (acc ? acc[this.helper.removeSquareBracketsFromProperty(property)] : undefined), item);
}

getColumns(): Array<DataColumn> {
return this.adapter.getColumns();
}

getRows(): DataRow[] {
if (this.isDataSourceValid()) {
return this.rows;
return this.adapter.getRows();
}

return [];
}

setRows(rows: Array<DataRow>): void {
this.adapter.setRows(rows);
}

setColumns(columns: Array<DataColumn>): void {
this.adapter.setColumns(columns);
}

getValue(row: DataRow, col: DataColumn, resolverFn?: (_row: DataRow, _col: DataColumn) => any): any {
return this.adapter.getValue(row, col, resolverFn);
}

getColumnType(row: DataRow, col: DataColumn): string {
return this.adapter.getColumnType(row, col);
}

getSorting(): DataSorting {
return this.adapter.getSorting();
}

setSorting(sorting: DataSorting): void {
this.adapter.setSorting(sorting);
}

sort(key?: string, direction?: string): void {
this.adapter.sort(key, direction);
}

isDataSourceValid(): boolean {
return this.hasAllColumnsLinkedToData() && this.hasAllMandatoryColumnPropertiesHaveValues();
return this.hasAllColumnsLinkedToData() && this.allMandatoryColumnPropertiesHaveValues();
}

private hasAllMandatoryColumnPropertiesHaveValues(): boolean {
return this.columns.every(column => !!column.key);
private allMandatoryColumnPropertiesHaveValues(): boolean {
return this.adapter.getColumns().every((column) => !!column.key);
}

private hasAllColumnsLinkedToData(): boolean {
const availableColumnKeys: string[] = this.columns.map(column => column.key);
const availableColumnKeys: string[] = this.adapter.getColumns().map((column) => column.key);

return availableColumnKeys.every(columnKey => this.rows.some(row => Object.keys(row.obj).includes(columnKey)));
return availableColumnKeys.every((columnKey) => this.adapter.getRows().some((row) => Object.keys(row.obj).includes(columnKey)));
}
}
Loading

0 comments on commit d0209d4

Please sign in to comment.