Skip to content

Commit

Permalink
Merge 48d457d into 644acfd
Browse files Browse the repository at this point in the history
  • Loading branch information
Darkein authored Jul 7, 2020
2 parents 644acfd + 48d457d commit ff07e47
Show file tree
Hide file tree
Showing 21 changed files with 358 additions and 117 deletions.
5 changes: 4 additions & 1 deletion integration/crud-typeorm/projects/projects.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Controller } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Crud } from '@nestjsx/crud';
import { Crud, OperatorsOptions, CustomOperators } from '@nestjsx/crud';

import { Project } from './project.entity';
import { ProjectsService } from './projects.service';
Expand All @@ -25,6 +25,9 @@ import { ProjectsService } from './projects.service';
users: {},
},
},
operators: {
custom: { custom: {query(field: string, param: string) => `${field} = :${param}`}}
}
})
@ApiTags('projects')
@Controller('/companies/:companyId/projects')
Expand Down
1 change: 1 addition & 0 deletions packages/crud-request/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './request-query-builder-options.interface';
export * from './params-options.interface';
export * from './parsed-request.interface';
export * from './create-query-params.interface';
export * from './operators-options.interface';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type CustomOperatorQuery = (field: string, param: string) => string;

export interface CustomOperators {
[key: string]: { isArray?: boolean };
}
54 changes: 39 additions & 15 deletions packages/crud-request/src/request-query.builder.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import {
hasValue,
isObject,
isString,
isArrayFull,
isNil,
isObject,
isString,
isUndefined,
} from '@nestjsx/util';
import { stringify } from 'qs';

import { RequestQueryBuilderOptions, CreateQueryParams } from './interfaces';
import {
CreateQueryParams,
CustomOperators,
RequestQueryBuilderOptions,
} from './interfaces';
import {
validateCondition,
validateFields,
Expand Down Expand Up @@ -70,9 +74,12 @@ export class RequestQueryBuilder {
return RequestQueryBuilder._options;
}

static create(params?: CreateQueryParams): RequestQueryBuilder {
static create(
params?: CreateQueryParams,
customOperators?: CustomOperators,
): RequestQueryBuilder {
const qb = new RequestQueryBuilder();
return isObject(params) ? qb.createFromParams(params) : qb;
return isObject(params) ? qb.createFromParams(params, customOperators) : qb;
}

get options(): RequestQueryBuilderOptions {
Expand Down Expand Up @@ -110,13 +117,19 @@ export class RequestQueryBuilder {
return this;
}

setFilter(f: QueryFilter | QueryFilterArr | Array<QueryFilter | QueryFilterArr>): this {
this.setCondition(f, 'filter');
setFilter(
f: QueryFilter | QueryFilterArr | Array<QueryFilter | QueryFilterArr>,
customOperators?: CustomOperators,
): this {
this.setCondition(f, 'filter', customOperators);
return this;
}

setOr(f: QueryFilter | QueryFilterArr | Array<QueryFilter | QueryFilterArr>): this {
this.setCondition(f, 'or');
setOr(
f: QueryFilter | QueryFilterArr | Array<QueryFilter | QueryFilterArr>,
customOperators?: CustomOperators,
): this {
this.setCondition(f, 'or', customOperators);
return this;
}

Expand Down Expand Up @@ -169,9 +182,14 @@ export class RequestQueryBuilder {
cond(
f: QueryFilter | QueryFilterArr,
cond: 'filter' | 'or' | 'search' = 'search',
customOperators?: CustomOperators,
): string {
const filter = Array.isArray(f) ? { field: f[0], operator: f[1], value: f[2] } : f;
validateCondition(filter, cond);
validateCondition(
filter,
cond,
this ? (this.options ? customOperators : undefined) : undefined,
);
const d = this.options.delim;

return (
Expand Down Expand Up @@ -199,11 +217,14 @@ export class RequestQueryBuilder {
return sort.field + ds + sort.order;
}

private createFromParams(params: CreateQueryParams): this {
private createFromParams(
params: CreateQueryParams,
customOperators: CustomOperators,
): this {
this.select(params.fields);
this.search(params.search);
this.setFilter(params.filter);
this.setOr(params.or);
this.setFilter(params.filter, customOperators);
this.setOr(params.or, customOperators);
this.setJoin(params.join);
this.setLimit(params.limit);
this.setOffset(params.offset);
Expand All @@ -229,14 +250,17 @@ export class RequestQueryBuilder {
private setCondition(
f: QueryFilter | QueryFilterArr | Array<QueryFilter | QueryFilterArr>,
cond: 'filter' | 'or',
customOperators: CustomOperators,
): void {
if (!isNil(f)) {
const param = this.checkQueryObjectParam(cond, []);
this.queryObject[param] = [
...this.queryObject[param],
...(Array.isArray(f) && !isString(f[0])
? (f as Array<QueryFilter | QueryFilterArr>).map((o) => this.cond(o, cond))
: [this.cond(f as QueryFilter | QueryFilterArr, cond)]),
? (f as Array<QueryFilter | QueryFilterArr>).map((o) =>
this.cond(o, cond, customOperators),
)
: [this.cond(f as QueryFilter | QueryFilterArr, cond, customOperators)]),
];
}
}
Expand Down
28 changes: 18 additions & 10 deletions packages/crud-request/src/request-query.parser.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import {
hasLength,
hasValue,
isString,
isArrayFull,
isDate,
isDateString,
isNil,
isObject,
isString,
isStringFull,
objKeys,
isNil,
ObjectLiteral,
objKeys,
} from '@nestjsx/util';

import { RequestQueryException } from './exceptions';
import {
CustomOperators,
ParamsOptions,
ParsedRequestParams,
RequestQueryBuilderOptions,
Expand Down Expand Up @@ -83,22 +84,25 @@ export class RequestQueryParser implements ParsedRequestParams {
};
}

parseQuery(query: any): this {
parseQuery(query: any, customOperators: CustomOperators = {}): this {
if (isObject(query)) {
const paramNames = objKeys(query);

if (hasLength(paramNames)) {
this._query = query;
this._paramNames = paramNames;
let searchData = this._query[this.getParamNames('search')[0]];
const searchData = this._query[this.getParamNames('search')[0]];

this.search = this.parseSearchQueryParam(searchData) as any;
if (isNil(this.search)) {
this.filter = this.parseQueryParam(
'filter',
this.conditionParser.bind(this, 'filter'),
this.conditionParser.bind(this, 'filter', customOperators),
);
this.or = this.parseQueryParam(
'or',
this.conditionParser.bind(this, 'or', customOperators),
);
this.or = this.parseQueryParam('or', this.conditionParser.bind(this, 'or'));
}
this.fields =
this.parseQueryParam('fields', this.fieldsParser.bind(this))[0] || [];
Expand Down Expand Up @@ -255,7 +259,11 @@ export class RequestQueryParser implements ParsedRequestParams {
}
}

private conditionParser(cond: 'filter' | 'or' | 'search', data: string): QueryFilter {
private conditionParser(
cond: 'filter' | 'or' | 'search',
customOperators: CustomOperators,
data: string,
): QueryFilter {
const isArrayValue = [
'in',
'notin',
Expand All @@ -265,7 +273,7 @@ export class RequestQueryParser implements ParsedRequestParams {
'$between',
'$inL',
'$notinL',
];
].concat(Object.keys(customOperators).filter((op) => customOperators[op].isArray));
const isEmptyValue = ['isnull', 'notnull', '$isnull', '$notnull'];
const param = data.split(this._options.delim);
const field = param[0];
Expand All @@ -283,7 +291,7 @@ export class RequestQueryParser implements ParsedRequestParams {
}

const condition: QueryFilter = { field, operator, value };
validateCondition(condition, cond);
validateCondition(condition, cond, customOperators);

return condition;
}
Expand Down
31 changes: 19 additions & 12 deletions packages/crud-request/src/request-query.validator.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import {
isUndefined,
isArrayStrings,
isStringFull,
isObject,
isEqual,
isNumber,
isNil,
isNumber,
isObject,
isStringFull,
isUndefined,
objKeys,
} from '@nestjsx/util';

import { RequestQueryException } from './exceptions';
import { ParamsOptions, ParamOption } from './interfaces';
import { CustomOperators, ParamOption, ParamsOptions } from './interfaces';
import {
ComparisonOperator,
CondOperator,
QueryFields,
QueryFilter,
ComparisonOperator,
QueryJoin,
QuerySort,
CondOperator,
} from './types';

export const deprecatedComparisonOperatorsList = [
Expand All @@ -44,7 +44,6 @@ export const comparisonOperatorsList = [

export const sortOrdersList = ['ASC', 'DESC'];

const comparisonOperatorsListStr = comparisonOperatorsList.join();
const sortOrdersListStr = sortOrdersList.join();

export function validateFields(fields: QueryFields): void {
Expand All @@ -56,19 +55,27 @@ export function validateFields(fields: QueryFields): void {
export function validateCondition(
val: QueryFilter,
cond: 'filter' | 'or' | 'search',
customOperators: CustomOperators,
): void {
if (!isObject(val) || !isStringFull(val.field)) {
throw new RequestQueryException(
`Invalid field type in ${cond} condition. String expected`,
);
}
validateComparisonOperator(val.operator);
validateComparisonOperator(val.operator, customOperators);
}

export function validateComparisonOperator(operator: ComparisonOperator): void {
if (!comparisonOperatorsList.includes(operator)) {
export function validateComparisonOperator(
operator: ComparisonOperator,
customOperators: CustomOperators = {},
): void {
const extendedComparisonOperatorsList = [
...comparisonOperatorsList,
...Object.keys(customOperators),
];
if (!extendedComparisonOperatorsList.includes(operator)) {
throw new RequestQueryException(
`Invalid comparison operator. ${comparisonOperatorsListStr} expected`,
`Invalid comparison operator. ${extendedComparisonOperatorsList.join()} expected`,
);
}
}
Expand Down
33 changes: 18 additions & 15 deletions packages/crud-request/src/types/request-query.types.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
export type QueryFields = string[];

export type QueryFilter = {
export interface QueryFilter {
field: string;
operator: ComparisonOperator;
value?: any;
};
}

export type QueryFilterArr = [string, ComparisonOperator, any?];

export type QueryJoin = {
export interface QueryJoin {
field: string;
select?: QueryFields;
};
}

export type QueryJoinArr = [string, QueryFields?];

export type QuerySort = {
export interface QuerySort {
field: string;
order: QuerySortOperator;
};
}

export type QuerySortArr = [string, QuerySortOperator];

Expand Down Expand Up @@ -67,14 +67,14 @@ export enum CondOperator {
NOT_IN_LOW = '$notinL',
}

export type ComparisonOperator = DeprecatedCondOperator | keyof SFieldOperator;
export type ComparisonOperator = DeprecatedCondOperator | keyof SFieldOperator | string;

// new search
export type SPrimitivesVal = string | number | boolean;

export type SFiledValues = SPrimitivesVal | Array<SPrimitivesVal>;
export type SFiledValues = SPrimitivesVal | SPrimitivesVal[];

export type SFieldOperator = {
export interface SFieldOperator {
$eq?: SFiledValues;
$ne?: SFiledValues;
$gt?: SFiledValues;
Expand All @@ -100,20 +100,23 @@ export type SFieldOperator = {
$notinL?: SFiledValues;
$or?: SFieldOperator;
$and?: never;
};
}

export type SField = SPrimitivesVal | SFieldOperator;
export type SField =
| SPrimitivesVal
| SFieldOperator
| { [$custom: string]: SFiledValues };

export type SFields = {
export interface SFields {
[key: string]: SField | Array<SFields | SConditionAND> | undefined;
$or?: Array<SFields | SConditionAND>;
$and?: never;
};
}

export type SConditionAND = {
export interface SConditionAND {
$and?: Array<SFields | SConditionAND>;
$or?: never;
};
}

export type SConditionKey = '$and' | '$or';

Expand Down
Loading

0 comments on commit ff07e47

Please sign in to comment.