Skip to content

Commit 3d3f604

Browse files
authored
Merge pull request #133 from nestjsx/fix/query-builder
Fix/query builder
2 parents 2ac4ba3 + 964f35c commit 3d3f604

9 files changed

+205
-51
lines changed

jest.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ module.exports = {
2424
],
2525
coverageThreshold: {
2626
global: {
27-
branches: 98,
28-
functions: 98,
29-
lines: 98,
30-
statements: 98,
27+
branches: 100,
28+
functions: 100,
29+
lines: 100,
30+
statements: 100,
3131
},
3232
},
3333
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { QueryFields, QueryFilter, QueryJoin, QuerySort } from '../types';
2+
3+
export interface CreateQueryParams {
4+
fields?: QueryFields;
5+
filter?: QueryFilter[];
6+
or?: QueryFilter[];
7+
join?: QueryJoin[];
8+
sort?: QuerySort[];
9+
limit?: number;
10+
offset?: number;
11+
page?: number;
12+
resetCache?: boolean;
13+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './request-query-builder-options.interface';
22
export * from './params-options.interface';
33
export * from './parsed-request.interface';
4+
export * from './create-query-params.interface';

packages/crud-request/src/interfaces/request-query-builder-options.interface.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ export interface RequestQueryBuilderOptions {
22
delim?: string;
33
delimStr?: string;
44
paramNamesMap?: {
5-
fields?: string[];
6-
filter?: string[];
7-
or?: string[];
8-
join?: string[];
9-
sort?: string[];
10-
limit?: string[];
11-
offset?: string[];
12-
page?: string[];
13-
cache?: string[];
5+
fields?: string | string[];
6+
filter?: string | string[];
7+
or?: string | string[];
8+
join?: string | string[];
9+
sort?: string | string[];
10+
limit?: string | string[];
11+
offset?: string | string[];
12+
page?: string | string[];
13+
cache?: string | string[];
1414
};
1515
}

packages/crud-request/src/request-query.builder.ts

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
import { hasLength, hasValue, isArrayFull, isNil } from '@nestjsx/util';
2-
3-
import { RequestQueryBuilderOptions } from './interfaces';
1+
import {
2+
hasLength,
3+
hasValue,
4+
isObject,
5+
isString,
6+
isArrayFull,
7+
isNil,
8+
} from '@nestjsx/util';
9+
10+
import { RequestQueryBuilderOptions, CreateQueryParams } from './interfaces';
411
import {
512
validateCondition,
613
validateFields,
@@ -17,14 +24,14 @@ export class RequestQueryBuilder {
1724
delimStr: ',',
1825
paramNamesMap: {
1926
fields: ['fields', 'select'],
20-
filter: ['filter[]', 'filter'],
21-
or: ['or[]', 'or'],
22-
join: ['join[]', 'join'],
23-
sort: ['sort[]', 'sort'],
27+
filter: 'filter',
28+
or: 'or',
29+
join: 'join',
30+
sort: 'sort',
2431
limit: ['per_page', 'limit'],
25-
offset: ['offset'],
26-
page: ['page'],
27-
cache: ['cache'],
32+
offset: 'offset',
33+
page: 'page',
34+
cache: 'cache',
2835
},
2936
};
3037

@@ -55,8 +62,9 @@ export class RequestQueryBuilder {
5562
return RequestQueryBuilder._options;
5663
}
5764

58-
static create(): RequestQueryBuilder {
59-
return new RequestQueryBuilder();
65+
static create(params?: CreateQueryParams): RequestQueryBuilder {
66+
const qb = new RequestQueryBuilder();
67+
return isObject(params) ? qb.createFromParams(params) : qb;
6068
}
6169

6270
get options(): RequestQueryBuilderOptions {
@@ -131,8 +139,50 @@ export class RequestQueryBuilder {
131139
return this;
132140
}
133141

142+
private createFromParams(params: CreateQueryParams): this {
143+
/* istanbul ignore else */
144+
if (isArrayFull(params.fields)) {
145+
this.select(params.fields);
146+
}
147+
/* istanbul ignore else */
148+
if (isArrayFull(params.filter)) {
149+
params.filter.forEach((filter) => this.setFilter(filter));
150+
}
151+
/* istanbul ignore else */
152+
if (isArrayFull(params.or)) {
153+
params.or.forEach((or) => this.setOr(or));
154+
}
155+
/* istanbul ignore else */
156+
if (isArrayFull(params.join)) {
157+
params.join.forEach((join) => this.setJoin(join));
158+
}
159+
/* istanbul ignore else */
160+
if (isArrayFull(params.sort)) {
161+
params.sort.forEach((sort) => this.sortBy(sort));
162+
}
163+
/* istanbul ignore else */
164+
if (!isNil(params.limit)) {
165+
this.setLimit(params.limit);
166+
}
167+
/* istanbul ignore else */
168+
if (!isNil(params.offset)) {
169+
this.setOffset(params.offset);
170+
}
171+
/* istanbul ignore else */
172+
if (!isNil(params.page)) {
173+
this.setPage(params.page);
174+
}
175+
/* istanbul ignore else */
176+
if (params.resetCache) {
177+
this.resetCache();
178+
}
179+
180+
return this;
181+
}
182+
134183
private getParamName(param: keyof RequestQueryBuilderOptions['paramNamesMap']): string {
135-
return this.options.paramNamesMap[param][0];
184+
const name = this.options.paramNamesMap[param];
185+
return isString(name) ? (name as string) : (name[0] as string);
136186
}
137187

138188
private getFields(): string {
@@ -153,12 +203,15 @@ export class RequestQueryBuilder {
153203

154204
const param = this.getParamName(cond);
155205
const d = this.options.delim;
206+
const br = this.addBrackets(this[`_${cond}`]);
156207

157208
return (
158209
this[`_${cond}`]
159210
.map(
160211
(f: QueryFilter) =>
161-
`${param}=${f.field}${d}${f.operator}${hasValue(f.value) ? d + f.value : ''}`,
212+
`${param}${br}=${f.field}${d}${f.operator}${
213+
hasValue(f.value) ? d + f.value : ''
214+
}`,
162215
)
163216
.join('&') + '&'
164217
);
@@ -172,12 +225,15 @@ export class RequestQueryBuilder {
172225
const param = this.getParamName('join');
173226
const d = this.options.delim;
174227
const ds = this.options.delimStr;
228+
const br = this.addBrackets(this._join);
175229

176230
return (
177231
this._join
178232
.map(
179233
(j: QueryJoin) =>
180-
`${param}=${j.field}${isArrayFull(j.select) ? d + j.select.join(ds) : ''}`,
234+
`${param}${br}=${j.field}${
235+
isArrayFull(j.select) ? d + j.select.join(ds) : ''
236+
}`,
181237
)
182238
.join('&') + '&'
183239
);
@@ -190,10 +246,12 @@ export class RequestQueryBuilder {
190246

191247
const param = this.getParamName('sort');
192248
const ds = this.options.delimStr;
249+
const br = this.addBrackets(this._sort);
193250

194251
return (
195-
this._sort.map((s: QuerySort) => `${param}=${s.field}${ds}${s.order}`).join('&') +
196-
'&'
252+
this._sort
253+
.map((s: QuerySort) => `${param}${br}=${s.field}${ds}${s.order}`)
254+
.join('&') + '&'
197255
);
198256
}
199257

@@ -207,4 +265,8 @@ export class RequestQueryBuilder {
207265

208266
return `${param}=${value}&`;
209267
}
268+
269+
private addBrackets(arr: any[]): string {
270+
return arr.length > 1 ? '[]' : '';
271+
}
210272
}

packages/crud-request/src/request-query.parser.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
hasLength,
33
hasValue,
4+
isString,
45
isArrayFull,
56
isDate,
67
isDateString,
@@ -129,9 +130,10 @@ export class RequestQueryParser implements ParsedRequestParams {
129130
private getParamNames(
130131
type: keyof RequestQueryBuilderOptions['paramNamesMap'],
131132
): string[] {
132-
return this._paramNames.filter((p) =>
133-
this._options.paramNamesMap[type].some((m) => m === p),
134-
);
133+
return this._paramNames.filter((p) => {
134+
const name = this._options.paramNamesMap[type];
135+
return isString(name) ? name === p : (name as string[]).some((m) => m === p);
136+
});
135137
}
136138

137139
private getParamValues(value: string | string[], parser: Function): string[] {

packages/crud-request/test/request-query.builder.spec.ts

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('#request-query', () => {
3434
});
3535
const paramNamesMap = (RequestQueryBuilder as any)._options.paramNamesMap;
3636
expect(paramNamesMap.fields[0]).toBe(override);
37-
expect(paramNamesMap.page[0]).toBe('page');
37+
expect(paramNamesMap.page).toBe('page');
3838
});
3939
it('should merge options, 2', () => {
4040
const override = 'override';
@@ -190,14 +190,21 @@ describe('#request-query', () => {
190190
const expected = 'override=foo,bar';
191191
expect(test).toBe(expected);
192192
});
193-
it('should return query with filter conditions', () => {
193+
it('should return query with filter conditions, 1', () => {
194194
const test = qb
195195
.setFilter({ field: 'foo', operator: 'eq', value: 'test' })
196196
.setFilter({ field: 'bar', operator: 'notnull' })
197197
.query();
198198
const expected = 'filter[]=foo||eq||test&filter[]=bar||notnull';
199199
expect(test).toBe(expected);
200200
});
201+
it('should return query with filter conditions, 2', () => {
202+
const test = qb
203+
.setFilter({ field: 'foo', operator: 'eq', value: 'test' })
204+
.query();
205+
const expected = 'filter=foo||eq||test';
206+
expect(test).toBe(expected);
207+
});
201208
it('should return query with or conditions', () => {
202209
const test = qb
203210
.setOr({ field: 'foo', operator: 'eq', value: 'test' })
@@ -243,5 +250,71 @@ describe('#request-query', () => {
243250
expect(test).toBe(expected);
244251
});
245252
});
253+
254+
describe('#createFromParams', () => {
255+
it('should set _fields', () => {
256+
const expected: QueryFields = ['geee', 'vooo'];
257+
let qb = RequestQueryBuilder.create({
258+
fields: expected,
259+
});
260+
expect((qb as any)._fields).toMatchObject(expected);
261+
});
262+
it('should set _filter', () => {
263+
const expected: QueryFilter = { field: 'foo', operator: CondOperator.EQUALS };
264+
let qb = RequestQueryBuilder.create({
265+
filter: [expected],
266+
});
267+
expect((qb as any)._filter[0]).toMatchObject(expected);
268+
});
269+
it('should set _or', () => {
270+
const expected: QueryFilter = { field: 'foo', operator: 'eq' };
271+
let qb = RequestQueryBuilder.create({
272+
or: [expected],
273+
});
274+
expect((qb as any)._or[0]).toMatchObject(expected);
275+
});
276+
it('should set _join', () => {
277+
const expected: QueryJoin = { field: 'foo', select: ['bar'] };
278+
let qb = RequestQueryBuilder.create({
279+
join: [expected],
280+
});
281+
expect((qb as any)._join[0]).toMatchObject(expected);
282+
});
283+
it('should set _sort', () => {
284+
const expected: QuerySort = { field: 'foo', order: 'ASC' };
285+
let qb = RequestQueryBuilder.create({
286+
sort: [expected],
287+
});
288+
expect((qb as any)._sort[0]).toMatchObject(expected);
289+
});
290+
it('should set _limit', () => {
291+
const expected = 10;
292+
let qb = RequestQueryBuilder.create({
293+
limit: expected,
294+
});
295+
expect((qb as any)._limit).toBe(expected);
296+
});
297+
it('should set _offset', () => {
298+
const expected = 10;
299+
let qb = RequestQueryBuilder.create({
300+
offset: expected,
301+
});
302+
expect((qb as any)._offset).toBe(expected);
303+
});
304+
it('should set _page', () => {
305+
const expected = 10;
306+
let qb = RequestQueryBuilder.create({
307+
page: expected,
308+
});
309+
expect((qb as any)._page).toBe(expected);
310+
});
311+
it('should set _cache', () => {
312+
const expected = 0;
313+
let qb = RequestQueryBuilder.create({
314+
resetCache: true,
315+
});
316+
expect((qb as any)._cache).toBe(expected);
317+
});
318+
});
246319
});
247320
});

0 commit comments

Comments
 (0)