nestjs-paginate 14.0.0 → 15.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -77
- package/lib/decorator.d.ts +2 -0
- package/lib/decorator.js +1 -0
- package/lib/decorator.js.map +1 -1
- package/lib/decorator.spec.js +11 -0
- package/lib/decorator.spec.js.map +1 -1
- package/lib/filter-expression.d.ts +54 -0
- package/lib/filter-expression.js +180 -0
- package/lib/filter-expression.js.map +1 -0
- package/lib/filter-expression.spec.d.ts +1 -0
- package/lib/filter-expression.spec.js +308 -0
- package/lib/filter-expression.spec.js.map +1 -0
- package/lib/filter.d.ts +38 -50
- package/lib/filter.js +217 -227
- package/lib/filter.js.map +1 -1
- package/lib/filter.spec.js +0 -81
- package/lib/filter.spec.js.map +1 -1
- package/lib/global-config.d.ts +6 -0
- package/lib/global-config.js +1 -0
- package/lib/global-config.js.map +1 -1
- package/lib/paginate.d.ts +9 -4
- package/lib/paginate.js +23 -9
- package/lib/paginate.js.map +1 -1
- package/lib/paginate.spec.js +358 -531
- package/lib/paginate.spec.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,11 +41,9 @@ updateGlobalConfig({
|
|
|
41
41
|
defaultOrigin: undefined,
|
|
42
42
|
defaultLimit: 20,
|
|
43
43
|
defaultMaxLimit: 100,
|
|
44
|
-
})
|
|
44
|
+
})
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
47
|
### Example
|
|
50
48
|
|
|
51
49
|
The following code exposes a route that can be utilized like so:
|
|
@@ -270,12 +268,12 @@ const paginateConfig: PaginateConfig<CatEntity> {
|
|
|
270
268
|
* Description: TypeORM partial selection. Limit selection further by using `select` query param.
|
|
271
269
|
* https://typeorm.io/select-query-builder#partial-selection
|
|
272
270
|
* Note: if you do not contain the primary key in the select array, primary key will be added automatically.
|
|
273
|
-
*
|
|
271
|
+
*
|
|
274
272
|
* Wildcard support:
|
|
275
273
|
* - Use '*' to select all columns from the main entity.
|
|
276
274
|
* - Use 'relation.*' to select all columns from a relation.
|
|
277
275
|
* - Use 'relation.subrelation.*' to select all columns from nested relations.
|
|
278
|
-
*
|
|
276
|
+
*
|
|
279
277
|
* Examples:
|
|
280
278
|
* select: ['*'] - Selects all columns from main entity
|
|
281
279
|
* select: ['id', 'name', 'toys.*'] - Selects id, name from main entity and all columns from toys relation
|
|
@@ -330,7 +328,7 @@ const paginateConfig: PaginateConfig<CatEntity> {
|
|
|
330
328
|
* Type: RelationColumn<CatEntity>
|
|
331
329
|
* Description: Indicates what relations of entity should be loaded.
|
|
332
330
|
*/
|
|
333
|
-
relations:
|
|
331
|
+
relations: {},
|
|
334
332
|
|
|
335
333
|
/**
|
|
336
334
|
* Required: false
|
|
@@ -495,7 +493,7 @@ http://localhost:3000/cats?filter.toys.name=$in:Mouse,String
|
|
|
495
493
|
|
|
496
494
|
```typescript
|
|
497
495
|
const config: PaginateConfig<CatEntity> = {
|
|
498
|
-
relations:
|
|
496
|
+
relations: { toys: true },
|
|
499
497
|
sortableColumns: ['id', 'name', 'toys.name'],
|
|
500
498
|
filterableColumns: {
|
|
501
499
|
'toys.name': [FilterOperator.IN],
|
|
@@ -511,7 +509,7 @@ const result = await paginate<CatEntity>(query, catRepo, config)
|
|
|
511
509
|
const config: PaginateConfig<CatEntity> = {
|
|
512
510
|
sortableColumns: ['id', 'name', 'toys.(size.height)', 'toys.(size.width)'],
|
|
513
511
|
searchableColumns: ['name'],
|
|
514
|
-
relations:
|
|
512
|
+
relations: { toys: true },
|
|
515
513
|
}
|
|
516
514
|
```
|
|
517
515
|
|
|
@@ -552,6 +550,7 @@ Quantifiers define how many related rows must satisfy the condition:
|
|
|
552
550
|
- `$none`: no related rows match the condition
|
|
553
551
|
|
|
554
552
|
### Examples
|
|
553
|
+
|
|
555
554
|
Assume `CatEntity` has a one‑to‑many relation `toys: CatToyEntity[]` where `CatToyEntity` has a string column `name`.
|
|
556
555
|
|
|
557
556
|
- At least one toy named exactly "Ball":
|
|
@@ -584,46 +583,20 @@ Assume `CatEntity` has a one‑to‑many relation `toys: CatToyEntity[]` where `
|
|
|
584
583
|
GET /cats?filter.toys.name=$any:$not:$eq:Squeaky
|
|
585
584
|
```
|
|
586
585
|
|
|
587
|
-
###
|
|
586
|
+
### Requiring ALL of several related values
|
|
588
587
|
|
|
589
|
-
|
|
590
|
-
|
|
588
|
+
To require that a parent has **all** of several related values, use a [`filter=` expression](#filter-expressions-filter)
|
|
589
|
+
with one term per value. Each relation term is an independent `EXISTS`, so ANDing them means
|
|
590
|
+
"has a toy named Ball **and** has a toy named Mouse":
|
|
591
591
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
```typescript
|
|
595
|
-
filterableColumns: {
|
|
596
|
-
'toys.name': [FilterOperator.EQ, FilterComparator.AND],
|
|
597
|
-
}
|
|
592
|
+
```url
|
|
593
|
+
GET /cats?filter=toys.name=$eq:Ball AND toys.name=$eq:Mouse
|
|
598
594
|
```
|
|
599
595
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
```url
|
|
603
|
-
GET /cats?filter.toys.name=$and:Ball&filter.toys.name=$and:Mouse
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
- Cat must have a toy named "Ball" **and** be friends with a cat named "Garfield" (two independent to-many paths):
|
|
607
|
-
|
|
608
|
-
```url
|
|
609
|
-
GET /cats?filter.toys.name=$and:Ball&filter.friends.name=$and:Garfield
|
|
610
|
-
```
|
|
611
|
-
|
|
612
|
-
**Restrictions and performance notes**:
|
|
596
|
+
This also composes across independent relation paths, e.g. has a Ball toy and is friends with Garfield:
|
|
613
597
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
- `$and` may not be combined with `$none` or `$all` quantifiers.
|
|
617
|
-
- Each `$and` value adds one correlated EXISTS subquery. For N values and a relation path of depth D, this produces N × D joins. The default cap is 20 values per sub-column; override with `maxAndValues` in `PaginateConfig`.
|
|
618
|
-
|
|
619
|
-
```typescript
|
|
620
|
-
const config: PaginateConfig<CatEntity> = {
|
|
621
|
-
sortableColumns: ['id'],
|
|
622
|
-
filterableColumns: {
|
|
623
|
-
'toys.name': [FilterOperator.EQ, FilterComparator.AND],
|
|
624
|
-
},
|
|
625
|
-
maxAndValues: 10, // optional, default is 20
|
|
626
|
-
}
|
|
598
|
+
```url
|
|
599
|
+
GET /cats?filter=toys.name=$eq:Ball AND friends.name=$eq:Garfield
|
|
627
600
|
```
|
|
628
601
|
|
|
629
602
|
## Usage with Eager Loading
|
|
@@ -678,7 +651,7 @@ or its nemesis's age when it has no best friend.
|
|
|
678
651
|
const config: PaginateConfig<CatEntity> = {
|
|
679
652
|
// Every column used in a group must be listed in sortableColumns.
|
|
680
653
|
sortableColumns: ['id', 'bestFriend.age', 'nemesis.age'],
|
|
681
|
-
relations:
|
|
654
|
+
relations: { bestFriend: true, nemesis: true },
|
|
682
655
|
}
|
|
683
656
|
```
|
|
684
657
|
|
|
@@ -739,27 +712,107 @@ const config: PaginateConfig<CatEntity> = {
|
|
|
739
712
|
|
|
740
713
|
`?filter.roles=$contains:moderator,admin` where column `roles` is an array and contains the values `moderator` and `admin`
|
|
741
714
|
|
|
715
|
+
### Filter expressions (`filter=`)
|
|
716
|
+
|
|
717
|
+
The per-column `filter.<column>=` parameters above are always combined with `AND`. For
|
|
718
|
+
arbitrary boolean logic, pass a single `filter=` expression instead. It uses the same
|
|
719
|
+
column and `$op:value` syntax, combined with `AND`, `OR`, `NOT` (case-insensitive,
|
|
720
|
+
precedence `NOT` > `AND` > `OR`) and parentheses:
|
|
721
|
+
|
|
722
|
+
```
|
|
723
|
+
?filter=color=$eq:black AND age=$gte:3
|
|
724
|
+
?filter=(color=$eq:black OR color=$eq:white) AND NOT name=$eq:Leche
|
|
725
|
+
?filter=home.name=$eq:House AND toys.name=$eq:String
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
- The columns and operators are validated against `filterableColumns`, exactly like the
|
|
729
|
+
per-column form. An unknown column or disallowed operator always returns `400 Bad Request`.
|
|
730
|
+
- Relation columns are matched with correlated `EXISTS` subqueries, so they compose under
|
|
731
|
+
`OR`/`NOT` and never join the relation into the result set. `NOT toys.name=$eq:Ball` means
|
|
732
|
+
"no matching toy exists".
|
|
733
|
+
- Root, embedded, [JSONB key-path](#jsonb-support), and [polymorphic (`~`)](#polymorphic-columns-)
|
|
734
|
+
columns are all valid leaves and apply as direct conditions. Every operator, suffix and
|
|
735
|
+
quantifier from the per-column form works unchanged (e.g. `age=$btw:3,5`, `age=$not:$null`,
|
|
736
|
+
`toys.name=$none:$eq:Ball`); JSONB filtering keeps its PostgreSQL/CockroachDB-only limitation.
|
|
737
|
+
- A value containing whitespace or parentheses must be quoted with `"` or `'`:
|
|
738
|
+
`?filter=home.name=$eq:"Cat Mansion"`.
|
|
739
|
+
|
|
740
|
+
Inside a quoted value, a backslash escapes a following quote or backslash (`\"`, `\'`, `\\`),
|
|
741
|
+
so a value can contain either quote character:
|
|
742
|
+
|
|
743
|
+
```
|
|
744
|
+
?filter=name=$eq:"Milo \"the cat\"" # value: Milo "the cat"
|
|
745
|
+
?filter=name=$eq:'it\'s mine' # value: it's mine
|
|
746
|
+
?filter=name=$eq:"a\"b\'c\\d" # value: a"b'c\d
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
Any other backslash is kept literal, so Windows paths and regexes need no doubling
|
|
750
|
+
(`?filter=path=$eq:"C:\Users"` → `C:\Users`). You can also switch quote styles instead of
|
|
751
|
+
escaping — `?filter=name=$eq:'O'"'"'Malley'`-style concatenation still works — but the
|
|
752
|
+
backslash form is usually clearer. Doubling a quote does **not** escape it
|
|
753
|
+
(`"say ""hi"""` yields `say hi`). Remember to URL-encode the `filter=` value; the examples
|
|
754
|
+
above are shown decoded for readability.
|
|
755
|
+
|
|
756
|
+
The value-level `$not` suffix (negating a single comparison, e.g. `color=$not:$eq:white`) is
|
|
757
|
+
distinct from the boolean `NOT` (negating a whole term or group).
|
|
758
|
+
|
|
759
|
+
Because the expression is parsed recursively, an unbounded expression is a denial-of-service
|
|
760
|
+
vector: a deeply nested or very wide payload can exhaust the call stack or blow up the
|
|
761
|
+
generated SQL. Every leaf, `AND`/`OR`/`NOT` operator, and parenthesised group counts as one
|
|
762
|
+
node, and the total is capped at **100** by default. Override the cap per endpoint with
|
|
763
|
+
`filterExpressionMaxComplexity` in the config, or globally via `updateGlobalConfig({ defaultFilterExpressionMaxComplexity })`.
|
|
764
|
+
An expression over the limit returns `400 Bad Request`.
|
|
765
|
+
|
|
766
|
+
```typescript
|
|
767
|
+
const config: PaginateConfig<CatEntity> = {
|
|
768
|
+
sortableColumns: ['id'],
|
|
769
|
+
filterableColumns: { color: true, name: true },
|
|
770
|
+
filterExpressionMaxComplexity: 50, // reject filter= expressions with more than 50 nodes
|
|
771
|
+
}
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
### Polymorphic columns (`~`)
|
|
775
|
+
|
|
776
|
+
A filter column may group several columns with `~` to filter on their `COALESCE` — the first
|
|
777
|
+
non-null value per row (the same `~` syntax as polymorphic sorting). This works in both the
|
|
778
|
+
`filter=` expression and the per-column form:
|
|
779
|
+
|
|
780
|
+
```url
|
|
781
|
+
?filter=bestFriend.age~nemesis.age=$eq:4
|
|
782
|
+
?filter.bestFriend.age~nemesis.age=$gte:5
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
Each part must be a plain or **to-one** relation column (not embedded, virtual, JSONB, or to-many).
|
|
786
|
+
Relation parts are left-joined automatically and are not added to the result set. Parts may be
|
|
787
|
+
**nested** (`a.b.c.leaf`) as long as every segment before the leaf is a to-one relation; each hop is
|
|
788
|
+
joined step by step, and a prefix shared across parts (e.g. two parts both starting `a.b`) reuses the
|
|
789
|
+
same join rather than colliding:
|
|
790
|
+
|
|
791
|
+
```url
|
|
792
|
+
?filter=bestFriend.home.street~home.street=$eq:Downtown
|
|
793
|
+
```
|
|
794
|
+
|
|
742
795
|
## JSONB Support
|
|
743
796
|
|
|
744
797
|
You can sort, search, and filter on JSONB columns using dot notation to access nested fields.
|
|
745
798
|
|
|
746
799
|
### Database support matrix
|
|
747
800
|
|
|
748
|
-
| Feature
|
|
749
|
-
|
|
750
|
-
| **Sorting** (`sortableColumns`)
|
|
751
|
-
| **Searching** (`searchableColumns`)
|
|
752
|
-
| **Filtering** (`$eq`, `$in`, `$contains`) | Yes (`@>` containment)
|
|
801
|
+
| Feature | PostgreSQL / CockroachDB | MySQL / MariaDB | SQLite |
|
|
802
|
+
| ----------------------------------------- | ------------------------ | --------------------------------------- | -------------------- |
|
|
803
|
+
| **Sorting** (`sortableColumns`) | Yes (`#>>`) | Yes (`JSON_UNQUOTE(JSON_EXTRACT(...))`) | Yes (`json_extract`) |
|
|
804
|
+
| **Searching** (`searchableColumns`) | Yes | Yes | Yes |
|
|
805
|
+
| **Filtering** (`$eq`, `$in`, `$contains`) | Yes (`@>` containment) | No | No |
|
|
753
806
|
|
|
754
807
|
> **Note:** Sorting and searching on JSONB paths is supported across all database engines — the library automatically uses the correct JSON extraction function for your DB. Filtering via `$eq`, `$in`, and `$contains` uses PostgreSQL's `@>` containment operator (via TypeORM's `JsonContains`) and is only supported on **PostgreSQL** and **CockroachDB**.
|
|
755
808
|
|
|
756
809
|
### Filtering operators (PostgreSQL / CockroachDB only)
|
|
757
810
|
|
|
758
|
-
| Operator
|
|
759
|
-
|
|
811
|
+
| Operator | Description |
|
|
812
|
+
| ----------- | -------------------------------------------------------- |
|
|
760
813
|
| `$eq` | Exact match via containment (`col @> '{"key":"value"}'`) |
|
|
761
|
-
| `$in` | Match any of a comma-separated list of values
|
|
762
|
-
| `$contains` | Match if a JSON array contains the value
|
|
814
|
+
| `$in` | Match any of a comma-separated list of values |
|
|
815
|
+
| `$contains` | Match if a JSON array contains the value |
|
|
763
816
|
|
|
764
817
|
### Direct JSONB column
|
|
765
818
|
|
|
@@ -781,7 +834,7 @@ where `settings` is a relation whose JSONB column `theme` is filtered.
|
|
|
781
834
|
|
|
782
835
|
```typescript
|
|
783
836
|
const config: PaginateConfig<UserEntity> = {
|
|
784
|
-
relations:
|
|
837
|
+
relations: { settings: true },
|
|
785
838
|
filterableColumns: {
|
|
786
839
|
'settings.theme': [FilterOperator.EQ, FilterOperator.IN],
|
|
787
840
|
},
|
|
@@ -817,29 +870,16 @@ Each value is expanded into its own `@>` condition joined with `OR`:
|
|
|
817
870
|
?filter.metadata.status=$not:$in:banned,suspended
|
|
818
871
|
```
|
|
819
872
|
|
|
820
|
-
##
|
|
821
|
-
|
|
822
|
-
Multi filters are filters that can be applied to a single column with a comparator.
|
|
823
|
-
|
|
824
|
-
### Examples
|
|
825
|
-
|
|
826
|
-
`?filter.createdAt=$gt:2022-02-02&filter.createdAt=$lt:2022-02-10` where column `createdAt` is after `2022-02-02` **and** before `2022-02-10`
|
|
873
|
+
## Combining filters on one column
|
|
827
874
|
|
|
828
|
-
|
|
875
|
+
Repeating a `filter.<column>=` parameter applies all of its conditions with **AND**, e.g. a range:
|
|
829
876
|
|
|
830
|
-
`?filter.
|
|
877
|
+
`?filter.createdAt=$gt:2022-02-02&filter.createdAt=$lt:2022-02-10` — `createdAt` after `2022-02-02` **and** before `2022-02-10`
|
|
831
878
|
|
|
832
|
-
**
|
|
879
|
+
For **OR** (within a column or across columns) and arbitrary boolean logic, use a
|
|
880
|
+
[`filter=` expression](#filter-expressions-filter):
|
|
833
881
|
|
|
834
|
-
`?filter
|
|
835
|
-
|
|
836
|
-
**Note:** The first comparator on the the first filter is ignored because the filters are grouped by the column name and chained with an `$and` to other filters.
|
|
837
|
-
|
|
838
|
-
`...&filter.id=5&filter.id=$or:7&filter.name=Milo&...`
|
|
839
|
-
|
|
840
|
-
is resolved to:
|
|
841
|
-
|
|
842
|
-
`WHERE ... AND (id = 5 OR id = 7) AND name = 'Milo' AND ...`
|
|
882
|
+
`?filter=id=$eq:5 OR id=$eq:7` — `id` equal to `5` **or** `7`
|
|
843
883
|
|
|
844
884
|
## Cursor-based Pagination
|
|
845
885
|
|
|
@@ -928,7 +968,6 @@ export function CustomSortBy(paginationConfig: PaginateConfig<any>) {
|
|
|
928
968
|
Now you can create your version of the whole docs decorator and use it
|
|
929
969
|
|
|
930
970
|
```typescript
|
|
931
|
-
|
|
932
971
|
const CustomApiPaginationQuery = (paginationConfig: PaginateConfig<any>) => {
|
|
933
972
|
return applyDecorators(
|
|
934
973
|
...[
|
|
@@ -946,13 +985,10 @@ const CustomApiPaginationQuery = (paginationConfig: PaginateConfig<any>) => {
|
|
|
946
985
|
function CustomPaginatedSwaggerDocs<DTO extends Type<unknown>>(dto: DTO, paginatedConfig: PaginateConfig<any>) {
|
|
947
986
|
return applyDecorators(ApiOkPaginatedResponse(dto, paginatedConfig), CustomApiPaginationQuery(paginatedConfig))
|
|
948
987
|
}
|
|
949
|
-
|
|
950
988
|
```
|
|
951
989
|
|
|
952
990
|
You can use CustomPaginatedSwaggerDocs instead of default PaginatedSwaggerDocs
|
|
953
991
|
|
|
954
|
-
|
|
955
|
-
|
|
956
992
|
## Troubleshooting
|
|
957
993
|
|
|
958
994
|
The package does not report error reasons in the response bodies. They are instead
|
package/lib/decorator.d.ts
CHANGED
package/lib/decorator.js
CHANGED
|
@@ -83,6 +83,7 @@ exports.Paginate = (0, common_1.createParamDecorator)((_data, ctx) => {
|
|
|
83
83
|
search: query.search ? query.search.toString() : undefined,
|
|
84
84
|
searchBy,
|
|
85
85
|
filter: Object.keys(filter).length ? filter : undefined,
|
|
86
|
+
filterExpression: (0, lodash_1.isString)(query.filter) ? query.filter : undefined,
|
|
86
87
|
select,
|
|
87
88
|
cursor: query.cursor ? query.cursor.toString() : undefined,
|
|
88
89
|
withDeleted: query.withDeleted === 'true' ? true : query.withDeleted === 'false' ? false : undefined,
|
package/lib/decorator.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorator.js","sourceRoot":"","sources":["../src/decorator.ts"],"names":[],"mappings":";;;AAAA,2CAAuE;AAGvE,mCAA8D;AAC9D,qCAAgC;AAEhC,SAAS,QAAQ,CAAC,IAAa;IAC3B,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;AAC5E,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAgB;IACtC,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,UAAU,CAAA;AACjE,CAAC;
|
|
1
|
+
{"version":3,"file":"decorator.js","sourceRoot":"","sources":["../src/decorator.ts"],"names":[],"mappings":"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"}
|
package/lib/decorator.spec.js
CHANGED
|
@@ -106,6 +106,7 @@ describe('Decorator', () => {
|
|
|
106
106
|
sortBy: undefined,
|
|
107
107
|
search: undefined,
|
|
108
108
|
searchBy: undefined,
|
|
109
|
+
filterExpression: undefined,
|
|
109
110
|
filter: undefined,
|
|
110
111
|
select: undefined,
|
|
111
112
|
cursor: undefined,
|
|
@@ -122,6 +123,7 @@ describe('Decorator', () => {
|
|
|
122
123
|
sortBy: undefined,
|
|
123
124
|
search: undefined,
|
|
124
125
|
searchBy: undefined,
|
|
126
|
+
filterExpression: undefined,
|
|
125
127
|
filter: undefined,
|
|
126
128
|
select: undefined,
|
|
127
129
|
cursor: undefined,
|
|
@@ -129,6 +131,12 @@ describe('Decorator', () => {
|
|
|
129
131
|
path: 'http://localhost/items',
|
|
130
132
|
});
|
|
131
133
|
});
|
|
134
|
+
it('should parse a bare filter= param into filterExpression', () => {
|
|
135
|
+
const context = expressContextFactory({ filter: 'color=$eq:black AND age=$gte:3' });
|
|
136
|
+
const result = decoratorfactory(null, context);
|
|
137
|
+
expect(result.filterExpression).toBe('color=$eq:black AND age=$gte:3');
|
|
138
|
+
expect(result.filter).toBeUndefined();
|
|
139
|
+
});
|
|
132
140
|
it('should handle express defined query fields', () => {
|
|
133
141
|
const context = expressContextFactory({
|
|
134
142
|
page: '1',
|
|
@@ -151,6 +159,7 @@ describe('Decorator', () => {
|
|
|
151
159
|
],
|
|
152
160
|
search: 'white',
|
|
153
161
|
searchBy: undefined,
|
|
162
|
+
filterExpression: undefined,
|
|
154
163
|
withDeleted: true,
|
|
155
164
|
select: ['name', 'createdAt'],
|
|
156
165
|
path: 'http://localhost/items',
|
|
@@ -193,6 +202,7 @@ describe('Decorator', () => {
|
|
|
193
202
|
],
|
|
194
203
|
search: 'white',
|
|
195
204
|
searchBy: undefined,
|
|
205
|
+
filterExpression: undefined,
|
|
196
206
|
withDeleted: false,
|
|
197
207
|
path: 'http://localhost/items',
|
|
198
208
|
filter: {
|
|
@@ -221,6 +231,7 @@ describe('Decorator', () => {
|
|
|
221
231
|
sortBy: [['NOTEXISTEN', 'BLABLA']],
|
|
222
232
|
search: 'white',
|
|
223
233
|
searchBy: undefined,
|
|
234
|
+
filterExpression: undefined,
|
|
224
235
|
withDeleted: undefined,
|
|
225
236
|
path: 'http://localhost/items',
|
|
226
237
|
filter: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorator.spec.js","sourceRoot":"","sources":["../src/decorator.spec.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"decorator.spec.js","sourceRoot":"","sources":["../src/decorator.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boolean filter expression parsed from a single `filter=` query parameter.
|
|
3
|
+
*
|
|
4
|
+
* Grammar (precedence: NOT > AND > OR), case-insensitive keywords:
|
|
5
|
+
* expr := or
|
|
6
|
+
* or := and (OR and)*
|
|
7
|
+
* and := not (AND not)*
|
|
8
|
+
* not := NOT not | primary
|
|
9
|
+
* primary := '(' expr ')' | leaf
|
|
10
|
+
* leaf := column '=' value // value uses the existing $op:operand syntax
|
|
11
|
+
*
|
|
12
|
+
* Tokens are whitespace-delimited; `(` and `)` are punctuation. A value containing
|
|
13
|
+
* whitespace or a `(` `)` must be quoted with `"` or `'`. Inside a quoted value a backslash
|
|
14
|
+
* escapes a following quote or backslash (`\"`, `\'`, `\\`); any other backslash is literal.
|
|
15
|
+
*/
|
|
16
|
+
export type FilterExpression = {
|
|
17
|
+
type: 'and';
|
|
18
|
+
children: FilterExpression[];
|
|
19
|
+
} | {
|
|
20
|
+
type: 'or';
|
|
21
|
+
children: FilterExpression[];
|
|
22
|
+
} | {
|
|
23
|
+
type: 'not';
|
|
24
|
+
child: FilterExpression;
|
|
25
|
+
} | {
|
|
26
|
+
type: 'leaf';
|
|
27
|
+
column: string;
|
|
28
|
+
value: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Default cap on the number of nodes (leaves, AND/OR/NOT operators, and parenthesised
|
|
32
|
+
* groups) a single `filter=` expression may contain. Because the parser is recursive, an
|
|
33
|
+
* unbounded expression is a denial-of-service vector: a deeply nested or very wide payload
|
|
34
|
+
* can exhaust the call stack or blow up the generated SQL. 100 nodes comfortably covers any
|
|
35
|
+
* realistic query while keeping the parse cheap. Override per-endpoint via
|
|
36
|
+
* `PaginateConfig.filterExpressionMaxComplexity`.
|
|
37
|
+
*/
|
|
38
|
+
export declare const DEFAULT_FILTER_EXPRESSION_MAX_COMPLEXITY = 100;
|
|
39
|
+
/** Filter expression with negation pushed onto the leaves, so only AND/OR groups remain. */
|
|
40
|
+
export type NormalizedFilterExpression = {
|
|
41
|
+
type: 'and';
|
|
42
|
+
children: NormalizedFilterExpression[];
|
|
43
|
+
} | {
|
|
44
|
+
type: 'or';
|
|
45
|
+
children: NormalizedFilterExpression[];
|
|
46
|
+
} | {
|
|
47
|
+
type: 'leaf';
|
|
48
|
+
column: string;
|
|
49
|
+
value: string;
|
|
50
|
+
negated: boolean;
|
|
51
|
+
};
|
|
52
|
+
export declare function parseFilterExpression(input: string, maxComplexity?: number): FilterExpression;
|
|
53
|
+
/** Pushes every NOT down onto the leaves via De Morgan, leaving only AND/OR groups. */
|
|
54
|
+
export declare function normalizeFilterExpression(node: FilterExpression, negated?: boolean): NormalizedFilterExpression;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_FILTER_EXPRESSION_MAX_COMPLEXITY = void 0;
|
|
4
|
+
exports.parseFilterExpression = parseFilterExpression;
|
|
5
|
+
exports.normalizeFilterExpression = normalizeFilterExpression;
|
|
6
|
+
const common_1 = require("@nestjs/common");
|
|
7
|
+
/**
|
|
8
|
+
* Default cap on the number of nodes (leaves, AND/OR/NOT operators, and parenthesised
|
|
9
|
+
* groups) a single `filter=` expression may contain. Because the parser is recursive, an
|
|
10
|
+
* unbounded expression is a denial-of-service vector: a deeply nested or very wide payload
|
|
11
|
+
* can exhaust the call stack or blow up the generated SQL. 100 nodes comfortably covers any
|
|
12
|
+
* realistic query while keeping the parse cheap. Override per-endpoint via
|
|
13
|
+
* `PaginateConfig.filterExpressionMaxComplexity`.
|
|
14
|
+
*/
|
|
15
|
+
exports.DEFAULT_FILTER_EXPRESSION_MAX_COMPLEXITY = 100;
|
|
16
|
+
const WHITESPACE = new Set([' ', '\t', '\n', '\r']);
|
|
17
|
+
function tokenize(input) {
|
|
18
|
+
const tokens = [];
|
|
19
|
+
let i = 0;
|
|
20
|
+
while (i < input.length) {
|
|
21
|
+
const c = input[i];
|
|
22
|
+
if (WHITESPACE.has(c)) {
|
|
23
|
+
i++;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (c === '(') {
|
|
27
|
+
tokens.push({ type: 'lparen' });
|
|
28
|
+
i++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (c === ')') {
|
|
32
|
+
tokens.push({ type: 'rparen' });
|
|
33
|
+
i++;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
let raw = '';
|
|
37
|
+
let quoted = false;
|
|
38
|
+
while (i < input.length) {
|
|
39
|
+
const ch = input[i];
|
|
40
|
+
if (WHITESPACE.has(ch) || ch === '(' || ch === ')')
|
|
41
|
+
break;
|
|
42
|
+
if (ch === '"' || ch === "'") {
|
|
43
|
+
quoted = true;
|
|
44
|
+
i++;
|
|
45
|
+
while (i < input.length && input[i] !== ch) {
|
|
46
|
+
// Inside a quoted span a backslash escapes a following quote or backslash,
|
|
47
|
+
// so either quote character (and a literal backslash) can appear without
|
|
48
|
+
// switching quote styles. Any other backslash is kept literal, leaving
|
|
49
|
+
// values such as Windows paths or regexes untouched.
|
|
50
|
+
if (input[i] === '\\' && i + 1 < input.length) {
|
|
51
|
+
const next = input[i + 1];
|
|
52
|
+
if (next === '"' || next === "'" || next === '\\') {
|
|
53
|
+
raw += next;
|
|
54
|
+
i += 2;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
raw += input[i];
|
|
59
|
+
i++;
|
|
60
|
+
}
|
|
61
|
+
if (i >= input.length) {
|
|
62
|
+
throw new common_1.BadRequestException('Unterminated quote in filter expression');
|
|
63
|
+
}
|
|
64
|
+
i++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
raw += ch;
|
|
68
|
+
i++;
|
|
69
|
+
}
|
|
70
|
+
if (!quoted) {
|
|
71
|
+
const keyword = raw.toUpperCase();
|
|
72
|
+
if (keyword === 'AND' || keyword === 'OR' || keyword === 'NOT') {
|
|
73
|
+
tokens.push({ type: keyword.toLowerCase() });
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
tokens.push({ type: 'leaf', raw });
|
|
78
|
+
}
|
|
79
|
+
return tokens;
|
|
80
|
+
}
|
|
81
|
+
function parse(tokens, maxComplexity) {
|
|
82
|
+
let pos = 0;
|
|
83
|
+
const peek = () => tokens[pos];
|
|
84
|
+
// Every leaf, AND/OR/NOT operator, and parenthesised descent counts as one unit of
|
|
85
|
+
// complexity. Tallied as the tree is built and checked eagerly, so a hostile payload is
|
|
86
|
+
// rejected before it can nest deep enough to overflow the recursion stack.
|
|
87
|
+
let complexity = 0;
|
|
88
|
+
const spend = () => {
|
|
89
|
+
if (++complexity > maxComplexity) {
|
|
90
|
+
throw new common_1.BadRequestException(`Filter expression is too complex (max ${maxComplexity} nodes)`);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
function parseOr() {
|
|
94
|
+
var _a;
|
|
95
|
+
const children = [parseAnd()];
|
|
96
|
+
while (((_a = peek()) === null || _a === void 0 ? void 0 : _a.type) === 'or') {
|
|
97
|
+
pos++;
|
|
98
|
+
spend();
|
|
99
|
+
children.push(parseAnd());
|
|
100
|
+
}
|
|
101
|
+
return children.length === 1 ? children[0] : { type: 'or', children };
|
|
102
|
+
}
|
|
103
|
+
function parseAnd() {
|
|
104
|
+
var _a;
|
|
105
|
+
const children = [parseNot()];
|
|
106
|
+
while (((_a = peek()) === null || _a === void 0 ? void 0 : _a.type) === 'and') {
|
|
107
|
+
pos++;
|
|
108
|
+
spend();
|
|
109
|
+
children.push(parseNot());
|
|
110
|
+
}
|
|
111
|
+
return children.length === 1 ? children[0] : { type: 'and', children };
|
|
112
|
+
}
|
|
113
|
+
function parseNot() {
|
|
114
|
+
var _a;
|
|
115
|
+
if (((_a = peek()) === null || _a === void 0 ? void 0 : _a.type) === 'not') {
|
|
116
|
+
pos++;
|
|
117
|
+
spend();
|
|
118
|
+
return { type: 'not', child: parseNot() };
|
|
119
|
+
}
|
|
120
|
+
return parsePrimary();
|
|
121
|
+
}
|
|
122
|
+
function parsePrimary() {
|
|
123
|
+
var _a;
|
|
124
|
+
const token = peek();
|
|
125
|
+
if (!token) {
|
|
126
|
+
throw new common_1.BadRequestException('Unexpected end of filter expression');
|
|
127
|
+
}
|
|
128
|
+
if (token.type === 'lparen') {
|
|
129
|
+
pos++;
|
|
130
|
+
spend();
|
|
131
|
+
const expr = parseOr();
|
|
132
|
+
if (((_a = peek()) === null || _a === void 0 ? void 0 : _a.type) !== 'rparen') {
|
|
133
|
+
throw new common_1.BadRequestException('Expected ")" in filter expression');
|
|
134
|
+
}
|
|
135
|
+
pos++;
|
|
136
|
+
return expr;
|
|
137
|
+
}
|
|
138
|
+
if (token.type === 'leaf') {
|
|
139
|
+
pos++;
|
|
140
|
+
spend();
|
|
141
|
+
const eq = token.raw.indexOf('=');
|
|
142
|
+
if (eq < 1) {
|
|
143
|
+
throw new common_1.BadRequestException(`Invalid filter expression term "${token.raw}", expected "column=value"`);
|
|
144
|
+
}
|
|
145
|
+
return { type: 'leaf', column: token.raw.slice(0, eq), value: token.raw.slice(eq + 1) };
|
|
146
|
+
}
|
|
147
|
+
throw new common_1.BadRequestException(`Unexpected "${token.type}" in filter expression`);
|
|
148
|
+
}
|
|
149
|
+
if (tokens.length === 0) {
|
|
150
|
+
throw new common_1.BadRequestException('Empty filter expression');
|
|
151
|
+
}
|
|
152
|
+
const expr = parseOr();
|
|
153
|
+
if (pos < tokens.length) {
|
|
154
|
+
throw new common_1.BadRequestException('Unexpected trailing tokens in filter expression');
|
|
155
|
+
}
|
|
156
|
+
return expr;
|
|
157
|
+
}
|
|
158
|
+
function parseFilterExpression(input, maxComplexity = exports.DEFAULT_FILTER_EXPRESSION_MAX_COMPLEXITY) {
|
|
159
|
+
return parse(tokenize(input), maxComplexity);
|
|
160
|
+
}
|
|
161
|
+
/** Pushes every NOT down onto the leaves via De Morgan, leaving only AND/OR groups. */
|
|
162
|
+
function normalizeFilterExpression(node, negated = false) {
|
|
163
|
+
switch (node.type) {
|
|
164
|
+
case 'leaf':
|
|
165
|
+
return { type: 'leaf', column: node.column, value: node.value, negated };
|
|
166
|
+
case 'not':
|
|
167
|
+
return normalizeFilterExpression(node.child, !negated);
|
|
168
|
+
case 'and':
|
|
169
|
+
return {
|
|
170
|
+
type: negated ? 'or' : 'and',
|
|
171
|
+
children: node.children.map((child) => normalizeFilterExpression(child, negated)),
|
|
172
|
+
};
|
|
173
|
+
case 'or':
|
|
174
|
+
return {
|
|
175
|
+
type: negated ? 'and' : 'or',
|
|
176
|
+
children: node.children.map((child) => normalizeFilterExpression(child, negated)),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=filter-expression.js.map
|