Skip to content

Commit

Permalink
fix(backend): able to preset filters on hidden columns & all queried
Browse files Browse the repository at this point in the history
- the backend services were using SlickGrid `getColumns` to get the column definitions but that returns only the visible columns, however we should be able to preset filters on hidden columns and also expect all columns to be queried (it's not because the column is hidden that we shouldn't be able to filter it and query it)
  • Loading branch information
ghiscoding committed May 21, 2021
1 parent 4df95fa commit c610979
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 36 deletions.
3 changes: 2 additions & 1 deletion packages/common/src/interfaces/backendService.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SingleColumnSort,
} from './index';
import { SlickGrid } from './slickGrid.interface';
import { SharedService } from '../services';

export interface BackendService {
/** Backend Service options */
Expand All @@ -26,7 +27,7 @@ export interface BackendService {
clearSorters?: () => void;

/** initialize the backend service with certain options */
init?: (serviceOptions?: BackendServiceOption | any, pagination?: Pagination, grid?: SlickGrid) => void;
init?: (serviceOptions?: BackendServiceOption | any, pagination?: Pagination, grid?: SlickGrid, sharedService?: SharedService) => void;

/** Get the dataset name */
getDatasetName?: () => string;
Expand Down
46 changes: 37 additions & 9 deletions packages/graphql/src/services/__tests__/graphql.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
MultiColumnSort,
OperatorType,
Pagination,
SharedService,
SlickGrid,
TranslaterService,
} from '@slickgrid-universal/common';
Expand Down Expand Up @@ -55,9 +56,11 @@ describe('GraphqlService', () => {
let service: GraphqlService;
let paginationOptions: Pagination;
let serviceOptions: GraphqlServiceOption;
let sharedService: SharedService;

beforeEach(() => {
mockColumns = [{ id: 'field1', field: 'field1', width: 100 }, { id: 'field2', field: 'field2', width: 100 }];
sharedService = new SharedService();
service = new GraphqlService();
serviceOptions = {
datasetName: 'users'
Expand Down Expand Up @@ -1145,17 +1148,20 @@ describe('GraphqlService', () => {
});

describe('presets', () => {
let mockColumns: Column[] = [];
beforeEach(() => {
const columns = [{ id: 'company', field: 'company' }, { id: 'gender', field: 'gender' }, { id: 'duration', field: 'duration', type: FieldType.number }, { id: 'startDate', field: 'startDate' }];
jest.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
mockColumns = [{ id: 'company', field: 'company' }, { id: 'gender', field: 'gender' }, { id: 'duration', field: 'duration', type: FieldType.number }, { id: 'startDate', field: 'startDate' }];
jest.spyOn(gridStub, 'getColumns').mockReturnValue(mockColumns);
});

afterEach(() => {
jest.clearAllMocks();
});

it('should return a query with search having a range of exclusive numbers when the search value contains 2 dots (..) to represent a range of numbers', () => {
const expectation = `query{users(first:10, offset:0, filterBy:[{field:duration, operator:GE, value:"2"}, {field:duration, operator:LE, value:"33"}]) {
totalCount,nodes{ id,company,gender,duration,startDate } }}`;
const presetFilters = [
{ columnId: 'duration', searchTerms: ['2..33'] },
] as CurrentFilter[];
const presetFilters = [{ columnId: 'duration', searchTerms: ['2..33'] }] as CurrentFilter[];

service.init(serviceOptions, paginationOptions, gridStub);
service.updateFilters(presetFilters, true);
Expand All @@ -1166,6 +1172,28 @@ describe('GraphqlService', () => {
expect(currentFilters).toEqual(presetFilters);
});

it('should return a query with all columns and search even when having hidden columns (basically when it is not part of the `getColumns()` return) when all passed are passed with the shared service', () => {
const expectation = `query{users(first:10, offset:0, filterBy:[{field:duration, operator:GE, value:"2"}, {field:duration, operator:LE, value:"33"}]) {
totalCount,nodes{ id,company,gender,duration,startDate } }}`;
const presetFilters = [{ columnId: 'duration', searchTerms: ['2..33'] }] as CurrentFilter[];
const mockColumnsCopy = [...mockColumns];

// remove "Gender" column from `getColumns` (to simulate hidden field)
mockColumnsCopy.splice(1, 1);
jest.spyOn(gridStub, 'getColumns').mockReturnValue(mockColumnsCopy);

// but still pass all columns to the service init
jest.spyOn(SharedService.prototype, 'allColumns', 'get').mockReturnValue(mockColumns);

service.init(serviceOptions, paginationOptions, gridStub, sharedService);
service.updateFilters(presetFilters, true);
const query = service.buildQuery();
const currentFilters = service.getCurrentFilters();

expect(removeSpaces(query)).toBe(removeSpaces(expectation));
expect(currentFilters).toEqual(presetFilters);
});

it('should return a query with a filter with range of numbers with decimals when the preset is a filter range with 2 dots (..) separator and range ends with a fraction', () => {
const expectation = `query{users(first:10, offset:0, filterBy:[{field:duration, operator:GE, value:"0.5"}, {field:duration, operator:LE, value:"0.88"}]) { totalCount,nodes{ id,company,gender,duration,startDate } }}`;
const presetFilters = [
Expand Down Expand Up @@ -1260,7 +1288,7 @@ describe('GraphqlService', () => {
const expectation = `query{users(first:10,offset:0,filterBy:[{field:duration,operator:EQ,value:"0.22"}]){totalCount,nodes{id,company,gender,duration,startDate}}}`;
const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column;
const mockColumnFilters = {
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['.22'] },
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['.22'], type: FieldType.string, },
} as ColumnFilters;

service.init(serviceOptions, paginationOptions, gridStub);
Expand All @@ -1274,7 +1302,7 @@ describe('GraphqlService', () => {
const expectation = `query{users(first:10,offset:0,filterBy:[{field:duration,operator:EQ,value:"-22"}]){totalCount,nodes{id,company,gender,duration,startDate}}}`;
const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.float } as Column;
const mockColumnFilters = {
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-2a2'] },
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-2a2'], type: FieldType.string, },
} as ColumnFilters;

service.init(serviceOptions, paginationOptions, gridStub);
Expand All @@ -1288,7 +1316,7 @@ describe('GraphqlService', () => {
const expectation = `query{users(first:10,offset:0,filterBy:[{field:duration,operator:EQ,value:"22"}]){totalCount,nodes{id,company,gender,duration,startDate}}}`;
const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.integer } as Column;
const mockColumnFilters = {
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['22;'] },
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['22;'], type: FieldType.string, },
} as ColumnFilters;

service.init(serviceOptions, paginationOptions, gridStub);
Expand All @@ -1302,7 +1330,7 @@ describe('GraphqlService', () => {
const expectation = `query{users(first:10,offset:0,filterBy:[{field:duration,operator:EQ,value:"0"}]){totalCount,nodes{id,company,gender,duration,startDate}}}`;
const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column;
const mockColumnFilters = {
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-'] },
duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-'], type: FieldType.string, },
} as ColumnFilters;

service.init(serviceOptions, paginationOptions, gridStub);
Expand Down
9 changes: 5 additions & 4 deletions packages/graphql/src/services/graphql.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
OperatorType,
Pagination,
PaginationChangedArgs,
SharedService,
SingleColumnSort,
SlickGrid,
SortDirection,
Expand All @@ -43,7 +44,7 @@ export class GraphqlService implements BackendService {
private _currentFilters: ColumnFilters | CurrentFilter[] = [];
private _currentPagination: CurrentPagination | null = null;
private _currentSorters: CurrentSorter[] = [];
private _columnDefinitions!: Column[];
private _columnDefinitions?: Column[];
private _grid: SlickGrid | undefined;
private _datasetIdPropName = 'id';
options: GraphqlServiceOption | undefined;
Expand All @@ -64,14 +65,14 @@ export class GraphqlService implements BackendService {
}

/** Initialization of the service, which acts as a constructor */
init(serviceOptions?: GraphqlServiceOption, pagination?: Pagination, grid?: SlickGrid): void {
init(serviceOptions?: GraphqlServiceOption, pagination?: Pagination, grid?: SlickGrid, sharedService?: SharedService): void {
this._grid = grid;
this.options = serviceOptions || { datasetName: '' };
this.pagination = pagination;
this._datasetIdPropName = this._gridOptions.datasetIdPropertyName || 'id';

if (grid && grid.getColumns) {
this._columnDefinitions = grid.getColumns() || [];
this._columnDefinitions = sharedService?.allColumns ?? grid.getColumns() ?? [];
}
}

Expand Down Expand Up @@ -525,7 +526,7 @@ export class GraphqlService implements BackendService {

// display the correct sorting icons on the UI, for that it requires (columnId, sortAsc) properties
const tmpSorterArray = currentSorters.map((sorter) => {
const columnDef = this._columnDefinitions.find((column: Column) => column.id === sorter.columnId);
const columnDef = this._columnDefinitions?.find((column: Column) => column.id === sorter.columnId);

graphqlSorters.push({
field: columnDef ? ((columnDef.queryFieldSorter || columnDef.queryField || columnDef.field) + '') : (sorter.columnId + ''),
Expand Down
33 changes: 30 additions & 3 deletions packages/odata/src/services/__tests__/grid-odata.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
OperatorType,
FieldType,
CurrentSorter,
SharedService,
SlickGrid,
BackendService,
} from '@slickgrid-universal/common';
Expand Down Expand Up @@ -48,8 +49,10 @@ describe('GridOdataService', () => {
let service: GridOdataService;
let paginationOptions: Pagination;
let serviceOptions: OdataOption;
let sharedService: SharedService;

beforeEach(() => {
sharedService = new SharedService();
service = new GridOdataService();
serviceOptions = {
orderBy: '',
Expand Down Expand Up @@ -1455,6 +1458,10 @@ describe('GridOdataService', () => {
});

describe('presets', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should return a query when using presets sorters array', () => {
const expectation = `$top=10&$orderby=Company desc,FirstName asc`;
const presets = [
Expand All @@ -1475,9 +1482,7 @@ describe('GridOdataService', () => {
const columns = [{ id: 'company', field: 'company' }, { id: 'gender', field: 'gender' }, { id: 'duration', field: 'duration', type: FieldType.number }];
jest.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
const expectation = `$top=10&$filter=(Duration ge 4 and Duration le 88)`;
const presetFilters = [
{ columnId: 'duration', searchTerms: ['4..88'] },
] as CurrentFilter[];
const presetFilters = [{ columnId: 'duration', searchTerms: ['4..88'] }] as CurrentFilter[];

service.init(serviceOptions, paginationOptions, gridStub);
service.updateFilters(presetFilters, true);
Expand All @@ -1488,6 +1493,28 @@ describe('GridOdataService', () => {
expect(currentFilters).toEqual(presetFilters);
});

it('should return a query with all columns and search even when having hidden columns (basically when it is not part of the `getColumns()` return) when all passed are passed with the shared service', () => {
const mockColumns = [{ id: 'company', field: 'company' }, { id: 'gender', field: 'gender' }, { id: 'duration', field: 'duration', type: FieldType.number }];
const expectation = `$top=10&$filter=(Duration ge 4 and Duration le 88)`;
const presetFilters = [{ columnId: 'duration', searchTerms: ['4..88'] }] as CurrentFilter[];
const mockColumnsCopy = [...mockColumns];

// remove "Gender" column from `getColumns` (to simulate hidden field)
mockColumnsCopy.splice(1, 1);
jest.spyOn(gridStub, 'getColumns').mockReturnValue(mockColumnsCopy);

// but still pass all columns to the service init
jest.spyOn(SharedService.prototype, 'allColumns', 'get').mockReturnValue(mockColumns);

service.init(serviceOptions, paginationOptions, gridStub, sharedService);
service.updateFilters(presetFilters, true);
const query = service.buildQuery();
const currentFilters = service.getCurrentFilters();

expect(query).toBe(expectation);
expect(currentFilters).toEqual(presetFilters);
});

it('should return a query with a filter with range of numbers with decimals when the preset is a filter range with 2 dots (..) separator and range ends with a fraction', () => {
const columns = [{ id: 'company', field: 'company' }, { id: 'gender', field: 'gender' }, { id: 'duration', field: 'duration', type: FieldType.number }];
jest.spyOn(gridStub, 'getColumns').mockReturnValue(columns);
Expand Down
7 changes: 4 additions & 3 deletions packages/odata/src/services/grid-odata.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
OperatorType,
OperatorString,
SearchTerm,
SharedService,
SingleColumnSort,
SlickGrid,
} from '@slickgrid-universal/common';
Expand Down Expand Up @@ -68,7 +69,7 @@ export class GridOdataService implements BackendService {
this._odataService = new OdataQueryBuilderService();
}

init(serviceOptions?: Partial<OdataOption>, pagination?: Pagination, grid?: SlickGrid): void {
init(serviceOptions?: Partial<OdataOption>, pagination?: Pagination, grid?: SlickGrid, sharedService?: SharedService): void {
this._grid = grid;
const mergedOptions = { ...this.defaultOptions, ...serviceOptions };

Expand All @@ -90,8 +91,8 @@ export class GridOdataService implements BackendService {
this.pagination = pagination;

if (grid?.getColumns) {
this._columnDefinitions = grid.getColumns() || [];
this._columnDefinitions = this._columnDefinitions.filter((column: Column) => !column.excludeFromQuery);
const tmpColumnDefinitions = sharedService?.visibleColumns ?? grid.getColumns() ?? [];
this._columnDefinitions = tmpColumnDefinitions.filter((column: Column) => !column.excludeFromQuery);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1496,7 +1496,7 @@ describe('Slick-Vanilla-Grid-Bundle Component instantiated via Constructor', ()
component.initialization(divContainer, slickEventHandler);

expect(bindBackendSpy).toHaveBeenCalledWith(mockGrid);
expect(initSpy).toHaveBeenCalledWith(mockGraphqlOptions, mockPagination, mockGrid);
expect(initSpy).toHaveBeenCalledWith(mockGraphqlOptions, mockPagination, mockGrid, sharedService);
});

it('should call bind backend sorting when "enableSorting" is set', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ export class SlickVanillaGridBundle {
const backendApi = gridOptions.backendServiceApi;

if (backendApi?.service?.init) {
backendApi.service.init(backendApi.options, gridOptions.pagination, this.slickGrid);
backendApi.service.init(backendApi.options, gridOptions.pagination, this.slickGrid, this.sharedService);
}
}

Expand Down Expand Up @@ -1242,6 +1242,7 @@ export class SlickVanillaGridBundle {

// finally set the new presets columns (including checkbox selector if need be)
this.slickGrid.setColumns(gridColumns);
this.sharedService.visibleColumns = gridColumns;
}
}
}
Expand Down
Loading

0 comments on commit c610979

Please sign in to comment.