Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit 1cd0b47

Browse files
committed
fix(odata): filter with single quote should be escaped, fixes #328
1 parent 8f7233c commit 1cd0b47

File tree

2 files changed

+23
-5
lines changed

2 files changed

+23
-5
lines changed

src/app/modules/angular-slickgrid/services/__tests__/grid-odata.service.spec.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,22 @@ describe('GridOdataService', () => {
576576
expect(query).toBe(expectation);
577577
});
578578

579+
it('should escape single quote by doubling the quote when filter includes a single quote', () => {
580+
const expectation = `$top=10&$filter=(Gender eq 'female' and not substringof('abc''s', Company))`;
581+
const mockColumnGender = { id: 'gender', field: 'gender' } as Column;
582+
const mockColumnCompany = { id: 'company', field: 'company' } as Column;
583+
const mockColumnFilters = {
584+
gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ' },
585+
company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: [`abc's`], operator: OperatorType.notContains },
586+
} as ColumnFilters;
587+
588+
service.init(serviceOptions, paginationOptions, gridStub);
589+
service.updateFilters(mockColumnFilters, false);
590+
const query = service.buildQuery();
591+
592+
expect(query).toBe(expectation);
593+
});
594+
579595
it('should return a query with multiple filters and expect same query string result as previous test even with "isUpdatedByPreset" enabled', () => {
580596
const expectation = `$top=10&$filter=(Gender eq 'female' and substringof('abc', Company))`;
581597
const mockColumnGender = { id: 'gender', field: 'gender' } as Column;
@@ -1349,7 +1365,7 @@ describe('GridOdataService', () => {
13491365
expect(currentFilters).toEqual(presetFilters);
13501366
});
13511367

1352-
it('should return a query with a filter with range of numbers with decimals when the preset is a filter range with 3 dots (..) separator', () => {
1368+
it('should return a query with a filter with range of numbers with decimals when the preset is a filter range with 3 dots (...) separator', () => {
13531369
serviceOptions.columnDefinitions = [{ id: 'company', field: 'company' }, { id: 'gender', field: 'gender' }, { id: 'duration', field: 'duration', type: FieldType.number }];
13541370
const expectation = `$top=10&$filter=(Duration gt 0.5 and Duration lt .88)`;
13551371
const presetFilters = [

src/app/modules/angular-slickgrid/services/grid-odata.service.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,8 @@ export class GridOdataService implements BackendService {
327327
if (operator === 'IN') {
328328
// example:: (Stage eq "Expired" or Stage eq "Renewal")
329329
for (let j = 0, lnj = searchTerms.length; j < lnj; j++) {
330-
tmpSearchTerms.push(`${fieldName} eq '${searchTerms[j]}'`);
330+
const searchVal = searchTerms[j].replace(`'`, `''`);
331+
tmpSearchTerms.push(`${fieldName} eq '${searchVal}'`);
331332
}
332333
searchBy = tmpSearchTerms.join(' or ');
333334
if (!(typeof searchBy === 'string' && searchBy[0] === '(' && searchBy.slice(-1) === ')')) {
@@ -336,7 +337,8 @@ export class GridOdataService implements BackendService {
336337
} else {
337338
// example:: (Stage ne "Expired" and Stage ne "Renewal")
338339
for (let k = 0, lnk = searchTerms.length; k < lnk; k++) {
339-
tmpSearchTerms.push(`${fieldName} ne '${searchTerms[k]}'`);
340+
const searchVal = searchTerms[k].replace(`'`, `''`);
341+
tmpSearchTerms.push(`${fieldName} ne '${searchVal}'`);
340342
}
341343
searchBy = tmpSearchTerms.join(' and ');
342344
if (!(typeof searchBy === 'string' && searchBy[0] === '(' && searchBy.slice(-1) === ')')) {
@@ -349,7 +351,7 @@ export class GridOdataService implements BackendService {
349351
} else if (fieldType === FieldType.string) {
350352
// string field needs to be in single quotes
351353
if (operator === '' || operator === OperatorType.contains || operator === OperatorType.notContains) {
352-
searchBy = this.odataQueryVersionWrapper('substring', odataVersion, fieldName, searchTerms);
354+
searchBy = this.odataQueryVersionWrapper('substring', odataVersion, fieldName, searchValue);
353355
if (operator === OperatorType.notContains) {
354356
searchBy = `not ${searchBy}`;
355357
}
@@ -373,7 +375,7 @@ export class GridOdataService implements BackendService {
373375
// push to our temp array and also trim white spaces
374376
if (searchBy !== '') {
375377
searchByArray.push(searchBy.trim());
376-
this.saveColumnFilter(fieldName || '', fieldSearchValue, searchTerms);
378+
this.saveColumnFilter(fieldName || '', fieldSearchValue, searchValue);
377379
}
378380
}
379381
}

0 commit comments

Comments
 (0)