@ackplus/nest-crud-request 0.1.51 → 1.1.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 +6 -130
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/{src → dist}/lib/query-builder.d.ts +1 -0
- package/dist/lib/query-builder.d.ts.map +1 -0
- package/{src → dist}/lib/query-builder.js +23 -16
- package/{src → dist}/lib/relation-builder.d.ts +1 -0
- package/dist/lib/relation-builder.d.ts.map +1 -0
- package/{src → dist}/lib/relation-builder.js +2 -6
- package/dist/lib/types.d.ts +53 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +31 -0
- package/{src → dist}/lib/utils.d.ts +1 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +11 -0
- package/{src → dist}/lib/where-builder.d.ts +4 -0
- package/dist/lib/where-builder.d.ts.map +1 -0
- package/{src → dist}/lib/where-builder.js +30 -13
- package/eslint.config.mjs +22 -0
- package/jest.config.ts +10 -0
- package/package.json +2 -20
- package/project.json +46 -0
- package/src/index.ts +3 -0
- package/src/lib/query-builder.ts +189 -0
- package/src/lib/relation-builder.ts +68 -0
- package/src/lib/types.js +1 -0
- package/src/lib/types.js.map +1 -0
- package/src/lib/types.ts +61 -0
- package/src/lib/utils.ts +11 -0
- package/src/lib/where-builder.ts +159 -0
- package/src/test/query-builder-where.spec.ts +173 -0
- package/src/test/query-builder.spec.ts +140 -0
- package/src/test/relation-builder.spec.ts +32 -0
- package/src/test/where-builder-complex.spec.ts +173 -0
- package/tsconfig.json +17 -0
- package/tsconfig.lib.json +10 -0
- package/tsconfig.spec.json +15 -0
- package/src/index.js +0 -6
- package/src/lib/utils.js +0 -11
- package/tsconfig.tsbuildinfo +0 -1
- /package/{src/index.d.ts → dist/index.js} +0 -0
package/README.md
CHANGED
|
@@ -1,135 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# nest-crud-request
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This library was generated with [Nx](https://nx.dev).
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Building
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Query parameter construction
|
|
9
|
-
- Filter builder
|
|
10
|
-
- Sort builder
|
|
11
|
-
- Search builder
|
|
12
|
-
- Pagination builder
|
|
13
|
-
- Request validation
|
|
14
|
-
- Automatic URL parameter encoding
|
|
7
|
+
Run `nx build nest-crud-request` to build the library.
|
|
15
8
|
|
|
16
|
-
##
|
|
9
|
+
## Running unit tests
|
|
17
10
|
|
|
18
|
-
|
|
19
|
-
npm install @ackplus/nest-crud-request
|
|
20
|
-
# or
|
|
21
|
-
yarn add @ackplus/nest-crud-request
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Quick Start
|
|
25
|
-
|
|
26
|
-
1. Import the request builder:
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
import { CrudRequestBuilder } from '@ackplus/nest-crud-request';
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
2. Create and use the request builder:
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
// Create a new request builder
|
|
36
|
-
const request = new CrudRequestBuilder();
|
|
37
|
-
|
|
38
|
-
// Add pagination
|
|
39
|
-
request.paginate(1, 10);
|
|
40
|
-
|
|
41
|
-
// Add sorting
|
|
42
|
-
request.sort('name', 'asc');
|
|
43
|
-
|
|
44
|
-
// Add filters
|
|
45
|
-
request.filter('status', 'active');
|
|
46
|
-
|
|
47
|
-
// Add search
|
|
48
|
-
request.search('john');
|
|
49
|
-
|
|
50
|
-
// Build the request
|
|
51
|
-
const queryParams = request.build();
|
|
52
|
-
// Result: ?page=1&limit=10&sort=name&order=asc&filter[status]=active&search=john
|
|
53
|
-
|
|
54
|
-
// Use with your HTTP client
|
|
55
|
-
const response = await axios.get(`/api/users${queryParams}`);
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## API Documentation
|
|
59
|
-
|
|
60
|
-
### CrudRequestBuilder
|
|
61
|
-
|
|
62
|
-
The main class for building CRUD requests.
|
|
63
|
-
|
|
64
|
-
#### Methods
|
|
65
|
-
|
|
66
|
-
- `paginate(page: number, limit: number)` - Set pagination parameters
|
|
67
|
-
- `sort(field: string, order: 'asc' | 'desc')` - Add sort parameters
|
|
68
|
-
- `filter(field: string, value: any)` - Add filter conditions
|
|
69
|
-
- `search(term: string)` - Add search term
|
|
70
|
-
- `build()` - Build the request and return query string
|
|
71
|
-
|
|
72
|
-
### Types
|
|
73
|
-
|
|
74
|
-
- `CrudRequest` - Type definition for the request structure
|
|
75
|
-
- `Filter` - Type for filter conditions
|
|
76
|
-
- `Sort` - Type for sort conditions
|
|
77
|
-
- `Pagination` - Type for pagination parameters
|
|
78
|
-
|
|
79
|
-
## Examples
|
|
80
|
-
|
|
81
|
-
### Basic Usage
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
const request = new CrudRequestBuilder()
|
|
85
|
-
.paginate(1, 10)
|
|
86
|
-
.sort('createdAt', 'desc')
|
|
87
|
-
.filter('status', 'active')
|
|
88
|
-
.search('john')
|
|
89
|
-
.build();
|
|
90
|
-
|
|
91
|
-
// Use with fetch
|
|
92
|
-
const response = await fetch(`/api/users${request}`);
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Advanced Filtering
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
const request = new CrudRequestBuilder()
|
|
99
|
-
.filter('age', { $gt: 18 })
|
|
100
|
-
.filter('status', { $in: ['active', 'pending'] })
|
|
101
|
-
.build();
|
|
102
|
-
|
|
103
|
-
// Result: ?filter[age][$gt]=18&filter[status][$in]=active,pending
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Multiple Sorts
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
const request = new CrudRequestBuilder()
|
|
110
|
-
.sort('name', 'asc')
|
|
111
|
-
.sort('age', 'desc')
|
|
112
|
-
.build();
|
|
113
|
-
|
|
114
|
-
// Result: ?sort=name,age&order=asc,desc
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Complex Search
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
const request = new CrudRequestBuilder()
|
|
121
|
-
.search('john doe')
|
|
122
|
-
.filter('department', 'IT')
|
|
123
|
-
.paginate(1, 20)
|
|
124
|
-
.build();
|
|
125
|
-
|
|
126
|
-
// Result: ?search=john%20doe&filter[department]=IT&page=1&limit=20
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
## Contributing
|
|
130
|
-
|
|
131
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
132
|
-
|
|
133
|
-
## License
|
|
134
|
-
|
|
135
|
-
This project is licensed under the terms of the MIT license.
|
|
11
|
+
Run `nx test nest-crud-request` to execute the unit tests via [Jest](https://jestjs.io).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-builder.d.ts","sourceRoot":"","sources":["../../src/lib/query-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElE,OAAO,EAAgB,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGtE,qBAAa,YAAY;IAErB,OAAO,CAAC,OAAO,CAA2B;IAE1C,OAAO,CAAC,YAAY,CAAoC;IAExD,OAAO,CAAC,eAAe,CAA0C;gBAErD,OAAO,CAAC,EAAE,mBAAmB;IAMzC,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAO9C,YAAY,CAAC,OAAO,EAAE,mBAAmB,EAAE,IAAI,UAAQ,GAAG,IAAI;IAc9D,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAa1C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAY7C,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAKnF,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKtC,KAAK,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK3C,QAAQ,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK9C,OAAO,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK7C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,IAAI;IAQ1D,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOlC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK3B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK3B,cAAc,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI;IAK1C,cAAc,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI;IAK1C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAKlC,QAAQ,CAAC,uBAAuB,UAAQ;;;;;;;;;;;IAoDxC,MAAM;CAKT"}
|
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { RelationBuilder } from './relation-builder';
|
|
2
|
+
import { deepMerge } from './utils';
|
|
3
|
+
import { WhereBuilder } from './where-builder';
|
|
4
|
+
export class QueryBuilder {
|
|
5
|
+
options = {};
|
|
6
|
+
whereBuilder = new WhereBuilder();
|
|
7
|
+
relationBuilder = new RelationBuilder();
|
|
8
8
|
constructor(options) {
|
|
9
|
-
this.options = {};
|
|
10
|
-
this.whereBuilder = new where_builder_1.WhereBuilder();
|
|
11
|
-
this.relationBuilder = new relation_builder_1.RelationBuilder();
|
|
12
9
|
if (options) {
|
|
13
10
|
this.setOptions(options);
|
|
14
11
|
}
|
|
15
12
|
}
|
|
16
13
|
setOptions(options) {
|
|
17
14
|
this.options = options;
|
|
18
|
-
this.whereBuilder = new
|
|
19
|
-
this.relationBuilder = new
|
|
15
|
+
this.whereBuilder = new WhereBuilder(options.where);
|
|
16
|
+
this.relationBuilder = new RelationBuilder(options.relations);
|
|
20
17
|
return this;
|
|
21
18
|
}
|
|
22
19
|
mergeOptions(options, deep = false) {
|
|
23
20
|
let updatedOptions = {};
|
|
24
21
|
if (deep) {
|
|
25
|
-
updatedOptions =
|
|
22
|
+
updatedOptions = deepMerge(this.options, options);
|
|
26
23
|
}
|
|
27
24
|
else {
|
|
28
|
-
updatedOptions =
|
|
25
|
+
updatedOptions = {
|
|
26
|
+
...this.options,
|
|
27
|
+
...options,
|
|
28
|
+
};
|
|
29
29
|
}
|
|
30
30
|
this.setOptions(updatedOptions);
|
|
31
31
|
return this;
|
|
32
32
|
}
|
|
33
33
|
addSelect(fields) {
|
|
34
|
+
// Ensure select is always an array
|
|
34
35
|
if (!this.options.select || typeof this.options.select === 'string') {
|
|
35
36
|
this.options.select = [];
|
|
36
37
|
}
|
|
@@ -43,6 +44,7 @@ class QueryBuilder {
|
|
|
43
44
|
return this;
|
|
44
45
|
}
|
|
45
46
|
removeSelect(fields) {
|
|
47
|
+
// Ensure select is an array before filtering
|
|
46
48
|
if (this.options.select && Array.isArray(this.options.select)) {
|
|
47
49
|
if (Array.isArray(fields)) {
|
|
48
50
|
this.options.select = this.options.select.filter(field => !fields.includes(field));
|
|
@@ -107,7 +109,10 @@ class QueryBuilder {
|
|
|
107
109
|
return this;
|
|
108
110
|
}
|
|
109
111
|
toObject(constrainToNestedObject = false) {
|
|
110
|
-
const options =
|
|
112
|
+
const options = {
|
|
113
|
+
...this.options,
|
|
114
|
+
};
|
|
115
|
+
// Convert where conditions to JSON string
|
|
111
116
|
if (this.whereBuilder.hasConditions()) {
|
|
112
117
|
if (constrainToNestedObject) {
|
|
113
118
|
options.where = this.whereBuilder.toObject();
|
|
@@ -119,6 +124,7 @@ class QueryBuilder {
|
|
|
119
124
|
else {
|
|
120
125
|
delete options.where;
|
|
121
126
|
}
|
|
127
|
+
// Convert relations to JSON string
|
|
122
128
|
if (this.relationBuilder.hasRelations()) {
|
|
123
129
|
if (constrainToNestedObject) {
|
|
124
130
|
options.relations = this.relationBuilder.toObject();
|
|
@@ -130,6 +136,7 @@ class QueryBuilder {
|
|
|
130
136
|
else {
|
|
131
137
|
delete options.relations;
|
|
132
138
|
}
|
|
139
|
+
// Convert order to JSON string if it exists
|
|
133
140
|
if (options.order && Object.keys(options.order).length > 0) {
|
|
134
141
|
if (constrainToNestedObject) {
|
|
135
142
|
options.order = options.order;
|
|
@@ -141,6 +148,7 @@ class QueryBuilder {
|
|
|
141
148
|
else {
|
|
142
149
|
delete options.order;
|
|
143
150
|
}
|
|
151
|
+
// Convert select to JSON string if it exists
|
|
144
152
|
if (options.select && options.select.length > 0) {
|
|
145
153
|
if (constrainToNestedObject) {
|
|
146
154
|
options.select = options.select;
|
|
@@ -159,4 +167,3 @@ class QueryBuilder {
|
|
|
159
167
|
return JSON.stringify(obj);
|
|
160
168
|
}
|
|
161
169
|
}
|
|
162
|
-
exports.QueryBuilder = QueryBuilder;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relation-builder.d.ts","sourceRoot":"","sources":["../../src/lib/relation-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAuB,eAAe,EAAE,MAAM,SAAS,CAAC;AAG/E,qBAAa,eAAe;IAExB,OAAO,CAAC,SAAS,CAAsB;gBAE3B,SAAS,CAAC,EAAE,eAAe,GAAG,MAAM;IAOhD,YAAY,CAAC,SAAS,EAAE,eAAe,GAAG,IAAI;IAe9C,KAAK,IAAI,IAAI;IAKb,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAgB3E,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAK9B,YAAY,IAAI,OAAO;IAIvB,QAAQ,IAAI,cAAc;IAI1B,MAAM,IAAI,MAAM;CAInB"}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.RelationBuilder = void 0;
|
|
4
|
-
class RelationBuilder {
|
|
1
|
+
export class RelationBuilder {
|
|
2
|
+
relations = {};
|
|
5
3
|
constructor(relations) {
|
|
6
|
-
this.relations = {};
|
|
7
4
|
if (relations) {
|
|
8
5
|
const relationOptions = typeof relations === 'string' ? JSON.parse(relations) : relations;
|
|
9
6
|
this.setRelations(relationOptions);
|
|
@@ -59,4 +56,3 @@ class RelationBuilder {
|
|
|
59
56
|
return JSON.stringify(this.relations);
|
|
60
57
|
}
|
|
61
58
|
}
|
|
62
|
-
exports.RelationBuilder = RelationBuilder;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface QueryBuilderOptions {
|
|
2
|
+
[key: string]: any;
|
|
3
|
+
select?: string[] | string;
|
|
4
|
+
relations?: RelationOptions | string;
|
|
5
|
+
where?: WhereOptions | string;
|
|
6
|
+
order?: Record<string, OrderDirectionEnum> | string;
|
|
7
|
+
skip?: number;
|
|
8
|
+
take?: number;
|
|
9
|
+
withDeleted?: boolean;
|
|
10
|
+
onlyDeleted?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare enum WhereLogicalOperatorEnum {
|
|
13
|
+
AND = "$and",
|
|
14
|
+
OR = "$or"
|
|
15
|
+
}
|
|
16
|
+
export declare enum WhereOperatorEnum {
|
|
17
|
+
EQ = "$eq",
|
|
18
|
+
NOT_EQ = "$ne",
|
|
19
|
+
GT = "$gt",
|
|
20
|
+
GT_OR_EQ = "$gte",
|
|
21
|
+
LT = "$lt",
|
|
22
|
+
LT_OR_EQ = "$lte",
|
|
23
|
+
IN = "$in",
|
|
24
|
+
NOT_IN = "$notIn",
|
|
25
|
+
LIKE = "$like",
|
|
26
|
+
NOT_LIKE = "$notLike",
|
|
27
|
+
ILIKE = "$iLike",
|
|
28
|
+
NOT_ILIKE = "$notIlike",
|
|
29
|
+
IS_NULL = "$isNull",
|
|
30
|
+
IS_NOT_NULL = "$isNotNull",
|
|
31
|
+
BETWEEN = "$between",
|
|
32
|
+
NOT_BETWEEN = "$notBetween",
|
|
33
|
+
IS_TRUE = "$isTrue",
|
|
34
|
+
IS_FALSE = "$isFalse"
|
|
35
|
+
}
|
|
36
|
+
export declare enum OrderDirectionEnum {
|
|
37
|
+
ASC = "ASC",
|
|
38
|
+
DESC = "DESC"
|
|
39
|
+
}
|
|
40
|
+
export type WhereObject = {
|
|
41
|
+
[key: string]: any;
|
|
42
|
+
$and?: WhereObject | WhereObject[];
|
|
43
|
+
$or?: WhereObject | WhereObject[];
|
|
44
|
+
};
|
|
45
|
+
export type WhereOptions = WhereObject | WhereObject[];
|
|
46
|
+
export type RelationObjectValue = {
|
|
47
|
+
select?: string[];
|
|
48
|
+
where?: WhereObject | WhereObject[];
|
|
49
|
+
joinType?: 'left' | 'inner';
|
|
50
|
+
};
|
|
51
|
+
export type RelationObject = Record<string, RelationObjectValue | boolean>;
|
|
52
|
+
export type RelationOptions = string | string[] | RelationObject;
|
|
53
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC;IACrC,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,MAAM,CAAC;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,oBAAY,wBAAwB;IAChC,GAAG,SAAS;IACZ,EAAE,QAAQ;CACb;AAED,oBAAY,iBAAiB;IACzB,EAAE,QAAQ;IACV,MAAM,QAAQ;IACd,EAAE,QAAQ;IACV,QAAQ,SAAS;IACjB,EAAE,QAAQ;IACV,QAAQ,SAAS;IACjB,EAAE,QAAQ;IACV,MAAM,WAAW;IACjB,IAAI,UAAU;IACd,QAAQ,aAAa;IACrB,KAAK,WAAW;IAChB,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,WAAW,eAAe;IAC1B,OAAO,aAAa;IACpB,WAAW,gBAAgB;IAC3B,OAAO,YAAY;IACnB,QAAQ,aAAa;CACxB;AAED,oBAAY,kBAAkB;IAC1B,GAAG,QAAQ;IACX,IAAI,SAAS;CAChB;AAED,MAAM,MAAM,WAAW,GAAG;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,IAAI,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;IACnC,GAAG,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,WAAW,EAAE,CAAC;AAGvD,MAAM,MAAM,mBAAmB,GAAG;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,CAAC;AAE3E,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export var WhereLogicalOperatorEnum;
|
|
2
|
+
(function (WhereLogicalOperatorEnum) {
|
|
3
|
+
WhereLogicalOperatorEnum["AND"] = "$and";
|
|
4
|
+
WhereLogicalOperatorEnum["OR"] = "$or";
|
|
5
|
+
})(WhereLogicalOperatorEnum || (WhereLogicalOperatorEnum = {}));
|
|
6
|
+
export var WhereOperatorEnum;
|
|
7
|
+
(function (WhereOperatorEnum) {
|
|
8
|
+
WhereOperatorEnum["EQ"] = "$eq";
|
|
9
|
+
WhereOperatorEnum["NOT_EQ"] = "$ne";
|
|
10
|
+
WhereOperatorEnum["GT"] = "$gt";
|
|
11
|
+
WhereOperatorEnum["GT_OR_EQ"] = "$gte";
|
|
12
|
+
WhereOperatorEnum["LT"] = "$lt";
|
|
13
|
+
WhereOperatorEnum["LT_OR_EQ"] = "$lte";
|
|
14
|
+
WhereOperatorEnum["IN"] = "$in";
|
|
15
|
+
WhereOperatorEnum["NOT_IN"] = "$notIn";
|
|
16
|
+
WhereOperatorEnum["LIKE"] = "$like";
|
|
17
|
+
WhereOperatorEnum["NOT_LIKE"] = "$notLike";
|
|
18
|
+
WhereOperatorEnum["ILIKE"] = "$iLike";
|
|
19
|
+
WhereOperatorEnum["NOT_ILIKE"] = "$notIlike";
|
|
20
|
+
WhereOperatorEnum["IS_NULL"] = "$isNull";
|
|
21
|
+
WhereOperatorEnum["IS_NOT_NULL"] = "$isNotNull";
|
|
22
|
+
WhereOperatorEnum["BETWEEN"] = "$between";
|
|
23
|
+
WhereOperatorEnum["NOT_BETWEEN"] = "$notBetween";
|
|
24
|
+
WhereOperatorEnum["IS_TRUE"] = "$isTrue";
|
|
25
|
+
WhereOperatorEnum["IS_FALSE"] = "$isFalse";
|
|
26
|
+
})(WhereOperatorEnum || (WhereOperatorEnum = {}));
|
|
27
|
+
export var OrderDirectionEnum;
|
|
28
|
+
(function (OrderDirectionEnum) {
|
|
29
|
+
OrderDirectionEnum["ASC"] = "ASC";
|
|
30
|
+
OrderDirectionEnum["DESC"] = "DESC";
|
|
31
|
+
})(OrderDirectionEnum || (OrderDirectionEnum = {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,CAUvD"}
|
|
@@ -14,5 +14,9 @@ export declare class WhereBuilder {
|
|
|
14
14
|
toJson(): string;
|
|
15
15
|
private parseCondition;
|
|
16
16
|
private updateCondition;
|
|
17
|
+
/**
|
|
18
|
+
* Intelligently merge two condition objects, handling logical operators properly
|
|
19
|
+
*/
|
|
17
20
|
private mergeConditions;
|
|
18
21
|
}
|
|
22
|
+
//# sourceMappingURL=where-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"where-builder.d.ts","sourceRoot":"","sources":["../../src/lib/where-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAGtE,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC,CAAC;AAEjJ,qBAAa,YAAY;IAErB,OAAO,CAAC,WAAW,CAA2B;gBAElC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;IAIhD,OAAO,CAAC,UAAU;IAIlB,KAAK,IAAI,IAAI;IAKb,KAAK,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK3C,QAAQ,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK9C,OAAO,CAAC,GAAG,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAK7C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAehC,aAAa,IAAI,OAAO;IAIxB,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAI/B,MAAM,IAAI,MAAM;IAIhB,OAAO,CAAC,cAAc;IAuDtB,OAAO,CAAC,eAAe;IAUvB;;OAEG;IACH,OAAO,CAAC,eAAe;CA0B1B"}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const types_1 = require("./types");
|
|
5
|
-
class WhereBuilder {
|
|
1
|
+
import { WhereLogicalOperatorEnum, WhereOperatorEnum } from './types';
|
|
2
|
+
export class WhereBuilder {
|
|
3
|
+
whereObject = {};
|
|
6
4
|
constructor(where) {
|
|
7
|
-
this.whereObject = {};
|
|
8
5
|
this.whereObject = typeof where === 'string' ? JSON.parse(where) : where || {};
|
|
9
6
|
}
|
|
10
7
|
isOperator(value) {
|
|
@@ -19,11 +16,11 @@ class WhereBuilder {
|
|
|
19
16
|
return this;
|
|
20
17
|
}
|
|
21
18
|
andWhere(...args) {
|
|
22
|
-
this.parseCondition(
|
|
19
|
+
this.parseCondition(WhereLogicalOperatorEnum.AND, ...args);
|
|
23
20
|
return this;
|
|
24
21
|
}
|
|
25
22
|
orWhere(...args) {
|
|
26
|
-
this.parseCondition(
|
|
23
|
+
this.parseCondition(WhereLogicalOperatorEnum.OR, ...args);
|
|
27
24
|
return this;
|
|
28
25
|
}
|
|
29
26
|
removeWhere(field) {
|
|
@@ -31,7 +28,7 @@ class WhereBuilder {
|
|
|
31
28
|
let current = this.whereObject;
|
|
32
29
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
33
30
|
if (!current[keys[i]]) {
|
|
34
|
-
return this;
|
|
31
|
+
return this; // If the path doesn't exist, do nothing
|
|
35
32
|
}
|
|
36
33
|
current = current[keys[i]];
|
|
37
34
|
}
|
|
@@ -52,42 +49,49 @@ class WhereBuilder {
|
|
|
52
49
|
let operator;
|
|
53
50
|
let value;
|
|
54
51
|
if (args.length === 0) {
|
|
52
|
+
// Do nothing
|
|
55
53
|
}
|
|
56
54
|
else if (args.length === 1) {
|
|
57
55
|
const condition = args[0];
|
|
58
56
|
if (typeof condition === 'function') {
|
|
57
|
+
// If the condition is a function, create a new WhereBuilder and call the function with it
|
|
59
58
|
const builder = new WhereBuilder();
|
|
60
59
|
condition(builder);
|
|
61
60
|
this.updateCondition(builder.toObject(), type);
|
|
62
61
|
}
|
|
63
62
|
else if (condition instanceof WhereBuilder) {
|
|
63
|
+
// If the condition is a WhereBuilder
|
|
64
64
|
this.updateCondition(condition.toObject(), type);
|
|
65
65
|
}
|
|
66
66
|
else {
|
|
67
|
+
// If the condition is a simple object
|
|
67
68
|
this.updateCondition(condition, type);
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
else if (args.length === 2 || args.length === 3) {
|
|
71
72
|
if (args.length === 2) {
|
|
73
|
+
// if there are only two arguments, the operator is EQ
|
|
72
74
|
field = args[0];
|
|
73
75
|
value = args[1];
|
|
74
76
|
if (typeof value === 'object') {
|
|
75
77
|
const firstKey = Object.keys(value)[0];
|
|
78
|
+
// if the first key is a operator, update the value with the operator
|
|
76
79
|
if (firstKey.startsWith('$')) {
|
|
77
80
|
this.updateCondition({ [field]: value }, type);
|
|
78
81
|
}
|
|
79
82
|
else {
|
|
80
|
-
this.updateCondition({ [field]: { [
|
|
83
|
+
this.updateCondition({ [field]: { [WhereOperatorEnum.EQ]: value } }, type);
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
else if (this.isOperator(value)) {
|
|
84
87
|
this.updateCondition({ [field]: { [value]: true } }, type);
|
|
85
88
|
}
|
|
86
89
|
else {
|
|
87
|
-
this.updateCondition({ [field]: { [
|
|
90
|
+
this.updateCondition({ [field]: { [WhereOperatorEnum.EQ]: value } }, type);
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
else {
|
|
94
|
+
// if there are three arguments, the operator is the second argument
|
|
91
95
|
field = args[0];
|
|
92
96
|
operator = args[1];
|
|
93
97
|
value = args[2];
|
|
@@ -96,34 +100,47 @@ class WhereBuilder {
|
|
|
96
100
|
}
|
|
97
101
|
return this;
|
|
98
102
|
}
|
|
103
|
+
// private updateCondition(condition: Record<string, any>, type: '$and' | '$or'): void {
|
|
104
|
+
// this.whereObject[type] = [...(this.whereObject[type] || []), condition].filter(Boolean);
|
|
105
|
+
// }
|
|
99
106
|
updateCondition(condition, type) {
|
|
100
107
|
if (type === null) {
|
|
108
|
+
// For direct where conditions, we need to merge intelligently
|
|
101
109
|
this.mergeConditions(this.whereObject, condition);
|
|
102
110
|
}
|
|
103
111
|
else {
|
|
112
|
+
// For logical operators ($and, $or), add to the array
|
|
104
113
|
this.whereObject[type] = [...(this.whereObject[type] || []), condition].filter(Boolean);
|
|
105
114
|
}
|
|
106
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Intelligently merge two condition objects, handling logical operators properly
|
|
118
|
+
*/
|
|
107
119
|
mergeConditions(target, source) {
|
|
108
120
|
for (const key in source) {
|
|
109
121
|
const sourceValue = source[key];
|
|
110
122
|
if (key === '$and' || key === '$or') {
|
|
123
|
+
// Handle logical operators - merge arrays
|
|
111
124
|
if (target[key]) {
|
|
125
|
+
// If target already has this logical operator, merge the arrays
|
|
112
126
|
target[key] = [...(target[key] || []), ...(Array.isArray(sourceValue) ? sourceValue : [sourceValue])];
|
|
113
127
|
}
|
|
114
128
|
else {
|
|
129
|
+
// If target doesn't have this logical operator, set it
|
|
115
130
|
target[key] = Array.isArray(sourceValue) ? sourceValue : [sourceValue];
|
|
116
131
|
}
|
|
117
132
|
}
|
|
118
133
|
else {
|
|
134
|
+
// Handle regular field conditions
|
|
119
135
|
if (target[key] && typeof target[key] === 'object' && typeof sourceValue === 'object') {
|
|
120
|
-
target
|
|
136
|
+
// If both target and source have the same field with object values, merge them
|
|
137
|
+
target[key] = { ...target[key], ...sourceValue };
|
|
121
138
|
}
|
|
122
139
|
else {
|
|
140
|
+
// Otherwise, simply assign the value
|
|
123
141
|
target[key] = sourceValue;
|
|
124
142
|
}
|
|
125
143
|
}
|
|
126
144
|
}
|
|
127
145
|
}
|
|
128
146
|
}
|
|
129
|
-
exports.WhereBuilder = WhereBuilder;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import baseConfig from '../../eslint.base.config';
|
|
2
|
+
|
|
3
|
+
export default [
|
|
4
|
+
...baseConfig,
|
|
5
|
+
{
|
|
6
|
+
files: ['**/*.json'],
|
|
7
|
+
rules: {
|
|
8
|
+
'@nx/dependency-checks': [
|
|
9
|
+
'error',
|
|
10
|
+
{
|
|
11
|
+
ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}'],
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
languageOptions: {
|
|
16
|
+
parser: await import('jsonc-eslint-parser'),
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
ignores: ['**/out-tsc'],
|
|
21
|
+
},
|
|
22
|
+
];
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
displayName: 'nest-crud-request',
|
|
3
|
+
preset: '../../jest.preset.js',
|
|
4
|
+
testEnvironment: 'node',
|
|
5
|
+
transform: {
|
|
6
|
+
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
|
7
|
+
},
|
|
8
|
+
moduleFileExtensions: ['ts', 'js', 'html'],
|
|
9
|
+
coverageDirectory: '../../coverage/packages/nest-crud-request',
|
|
10
|
+
};
|
package/package.json
CHANGED
|
@@ -1,28 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ackplus/nest-crud-request",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"types": "./src/index.d.ts",
|
|
7
|
-
"license": "MIT",
|
|
8
|
-
"description": "Request parsing and query building utilities for @ackplus/nest-crud",
|
|
9
|
-
"keywords": [
|
|
10
|
-
"nestjs",
|
|
11
|
-
"crud",
|
|
12
|
-
"query",
|
|
13
|
-
"request",
|
|
14
|
-
"parser",
|
|
15
|
-
"builder"
|
|
16
|
-
],
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "https://github.com/ack-solutions/packages.git"
|
|
20
|
-
},
|
|
21
|
-
"homepage": "https://github.com/ack-solutions/packages.git#readme",
|
|
22
|
-
"publishConfig": {
|
|
23
|
-
"access": "public"
|
|
24
|
-
},
|
|
25
7
|
"dependencies": {
|
|
26
8
|
"tslib": "^2.3.0"
|
|
27
9
|
}
|
|
28
|
-
}
|
|
10
|
+
}
|
package/project.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nest-crud-request",
|
|
3
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"sourceRoot": "packages/nest-crud-request/src",
|
|
5
|
+
"projectType": "library",
|
|
6
|
+
"release": {
|
|
7
|
+
"version": {
|
|
8
|
+
"manifestRootsToUpdate": [
|
|
9
|
+
"dist/{projectRoot}"
|
|
10
|
+
],
|
|
11
|
+
"currentVersionResolver": "git-tag",
|
|
12
|
+
"fallbackCurrentVersionResolver": "disk"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"tags": [],
|
|
16
|
+
"targets": {
|
|
17
|
+
"build": {
|
|
18
|
+
"executor": "@nx/js:tsc",
|
|
19
|
+
"outputs": [
|
|
20
|
+
"{options.outputPath}"
|
|
21
|
+
],
|
|
22
|
+
"options": {
|
|
23
|
+
"outputPath": "dist/packages/nest-crud-request",
|
|
24
|
+
"main": "packages/nest-crud-request/src/index.ts",
|
|
25
|
+
"tsConfig": "packages/nest-crud-request/tsconfig.lib.json",
|
|
26
|
+
"assets": [
|
|
27
|
+
"packages/nest-crud-request/*.md"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"nx-release-publish": {
|
|
32
|
+
"options": {
|
|
33
|
+
"packageRoot": "dist/{projectRoot}"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"test": {
|
|
37
|
+
"executor": "@nx/jest:jest",
|
|
38
|
+
"outputs": [
|
|
39
|
+
"{workspaceRoot}/coverage/{projectRoot}"
|
|
40
|
+
],
|
|
41
|
+
"options": {
|
|
42
|
+
"jestConfig": "packages/nest-crud-request/jest.config.ts"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|