@drax/crud-back 0.11.5 → 0.12.2
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/dist/builders/CrudSchemaBuilder.js +349 -0
- package/dist/controllers/AbstractFastifyController.js +105 -176
- package/dist/index.js +15 -1
- package/dist/regexs/QueryFilterRegex.js +3 -0
- package/dist/repository/AbstractMongoRepository.js +114 -28
- package/dist/repository/AbstractSqliteRepository.js +131 -41
- package/dist/schemas/DeleteBodyResponseSchema.js +9 -0
- package/dist/schemas/ErrorBodyResponseSchema.js +16 -0
- package/dist/schemas/ExportBodyResponseSchema.js +9 -0
- package/dist/schemas/FindBySchema.js +6 -0
- package/dist/schemas/FindSchema.js +9 -0
- package/dist/schemas/IdParamSchema.js +6 -0
- package/dist/schemas/PaginateBodySchema.js +20 -0
- package/dist/schemas/PaginateSchema.js +17 -0
- package/dist/schemas/SearchSchema.js +5 -0
- package/dist/services/AbstractService.js +8 -5
- package/dist/zod/DeleteBodySchema.js +6 -0
- package/dist/zod/IdParamSchema.js +6 -0
- package/dist/zod/PaginateBodySchema.js +20 -0
- package/package.json +7 -6
- package/src/builders/CrudSchemaBuilder.ts +420 -0
- package/src/controllers/AbstractFastifyController.ts +149 -165
- package/src/index.ts +28 -0
- package/src/regexs/QueryFilterRegex.ts +4 -0
- package/src/repository/AbstractMongoRepository.ts +153 -40
- package/src/repository/AbstractSqliteRepository.ts +196 -72
- package/src/schemas/DeleteBodyResponseSchema.ts +12 -0
- package/src/schemas/ErrorBodyResponseSchema.ts +20 -0
- package/src/schemas/ExportBodyResponseSchema.ts +12 -0
- package/src/schemas/FindBySchema.ts +9 -0
- package/src/schemas/FindSchema.ts +13 -0
- package/src/schemas/IdParamSchema.ts +9 -0
- package/src/schemas/PaginateSchema.ts +21 -0
- package/src/schemas/SearchSchema.ts +8 -0
- package/src/services/AbstractService.ts +42 -33
- package/tsconfig.tsbuildinfo +1 -1
- package/types/builders/CrudSchemaBuilder.d.ts +2401 -0
- package/types/builders/CrudSchemaBuilder.d.ts.map +1 -0
- package/types/controllers/AbstractFastifyController.d.ts +6 -1
- package/types/controllers/AbstractFastifyController.d.ts.map +1 -1
- package/types/index.d.ts +10 -1
- package/types/index.d.ts.map +1 -1
- package/types/regexs/QueryFilterRegex.d.ts +4 -0
- package/types/regexs/QueryFilterRegex.d.ts.map +1 -0
- package/types/repository/AbstractMongoRepository.d.ts +6 -3
- package/types/repository/AbstractMongoRepository.d.ts.map +1 -1
- package/types/repository/AbstractSqliteRepository.d.ts +23 -8
- package/types/repository/AbstractSqliteRepository.d.ts.map +1 -1
- package/types/schemas/DeleteBodyResponseSchema.d.ts +20 -0
- package/types/schemas/DeleteBodyResponseSchema.d.ts.map +1 -0
- package/types/schemas/ErrorBodyResponseSchema.d.ts +60 -0
- package/types/schemas/ErrorBodyResponseSchema.d.ts.map +1 -0
- package/types/schemas/ExportBodyResponseSchema.d.ts +20 -0
- package/types/schemas/ExportBodyResponseSchema.d.ts.map +1 -0
- package/types/schemas/FindBySchema.d.ts +13 -0
- package/types/schemas/FindBySchema.d.ts.map +1 -0
- package/types/schemas/FindSchema.d.ts +19 -0
- package/types/schemas/FindSchema.d.ts.map +1 -0
- package/types/schemas/IdParamSchema.d.ts +11 -0
- package/types/schemas/IdParamSchema.d.ts.map +1 -0
- package/types/schemas/PaginateBodySchema.d.ts +61 -0
- package/types/schemas/PaginateBodySchema.d.ts.map +1 -0
- package/types/schemas/PaginateSchema.d.ts +41 -0
- package/types/schemas/PaginateSchema.d.ts.map +1 -0
- package/types/schemas/SearchSchema.d.ts +10 -0
- package/types/schemas/SearchSchema.d.ts.map +1 -0
- package/types/services/AbstractService.d.ts +5 -5
- package/types/services/AbstractService.d.ts.map +1 -1
- package/types/zod/DeleteBodySchema.d.ts +11 -0
- package/types/zod/DeleteBodySchema.d.ts.map +1 -0
- package/types/zod/IdParamSchema.d.ts +11 -0
- package/types/zod/IdParamSchema.d.ts.map +1 -0
- package/types/zod/PaginateBodySchema.d.ts +61 -0
- package/types/zod/PaginateBodySchema.d.ts.map +1 -0
|
@@ -1,103 +1,190 @@
|
|
|
1
1
|
import "mongoose-paginate-v2";
|
|
2
|
-
import mongoose
|
|
3
|
-
import {
|
|
2
|
+
import mongoose from "mongoose";
|
|
3
|
+
import type {Cursor, PopulateOptions} from "mongoose";
|
|
4
|
+
import {
|
|
5
|
+
MongooseQueryFilter,
|
|
6
|
+
MongooseSort,
|
|
7
|
+
MongooseValidationErrorToValidationError,
|
|
8
|
+
MongooseCastErrorToValidationError,
|
|
9
|
+
MongoServerErrorToValidationError
|
|
10
|
+
} from "@drax/common-back";
|
|
4
11
|
import type {DeleteResult} from "mongodb";
|
|
5
12
|
import type {IDraxPaginateOptions, IDraxPaginateResult, IDraxFindOptions, IDraxCrud, IDraxFieldFilter} from "@drax/crud-share";
|
|
6
13
|
import type {PaginateModel, PaginateOptions, PaginateResult} from "mongoose";
|
|
14
|
+
import {InvalidIdError} from "@drax/common-back";
|
|
15
|
+
import {MongoServerError} from "mongodb";
|
|
7
16
|
|
|
8
17
|
|
|
9
18
|
class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
10
19
|
|
|
11
20
|
protected _model: mongoose.Model<T> & PaginateModel<T>
|
|
12
21
|
protected _searchFields: string[] = []
|
|
13
|
-
protected _populateFields:
|
|
22
|
+
protected _populateFields: string[] | PopulateOptions[] = []
|
|
23
|
+
protected _lean: boolean = true
|
|
24
|
+
|
|
25
|
+
assertId(id: string): void {
|
|
26
|
+
if(!mongoose.Types.ObjectId.isValid(id)){
|
|
27
|
+
console.log(`Invalid ID: ${id} is not a valid ObjectId.`)
|
|
28
|
+
throw new InvalidIdError(id)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
14
31
|
|
|
15
32
|
|
|
16
33
|
async create(data: C): Promise<T> {
|
|
17
34
|
try {
|
|
18
|
-
const item: mongoose.HydratedDocument<T> =
|
|
19
|
-
await item.save()
|
|
35
|
+
const item: mongoose.HydratedDocument<T> = await this._model.create(data)
|
|
20
36
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
if(this._populateFields && this._populateFields.length > 0){
|
|
38
|
+
//@ts-ignore
|
|
39
|
+
await item.populate(this._populateFields)
|
|
40
|
+
}
|
|
41
|
+
return this._lean ? item : item.toObject()
|
|
25
42
|
} catch (e) {
|
|
26
43
|
if (e instanceof mongoose.Error.ValidationError) {
|
|
27
|
-
throw
|
|
44
|
+
throw MongooseValidationErrorToValidationError(e)
|
|
45
|
+
}
|
|
46
|
+
if (e instanceof mongoose.Error.CastError) {
|
|
47
|
+
throw MongooseCastErrorToValidationError(e)
|
|
48
|
+
}
|
|
49
|
+
if(e instanceof MongoServerError || e.name === 'MongoServerError'){
|
|
50
|
+
throw MongoServerErrorToValidationError(e)
|
|
28
51
|
}
|
|
29
52
|
throw e
|
|
30
53
|
}
|
|
31
54
|
}
|
|
32
55
|
|
|
33
56
|
async update(id: string, data: U): Promise<T> {
|
|
57
|
+
|
|
58
|
+
this.assertId(id)
|
|
59
|
+
|
|
34
60
|
try {
|
|
35
61
|
const item: mongoose.HydratedDocument<T> = await this._model.findOneAndUpdate({_id: id}, data, {new: true}).populate(this._populateFields).exec()
|
|
36
|
-
|
|
62
|
+
|
|
63
|
+
return this._lean ? item : item.toObject()
|
|
37
64
|
} catch (e) {
|
|
38
65
|
if (e instanceof mongoose.Error.ValidationError) {
|
|
39
|
-
throw
|
|
66
|
+
throw MongooseValidationErrorToValidationError(e)
|
|
67
|
+
}
|
|
68
|
+
if (e instanceof mongoose.Error.CastError) {
|
|
69
|
+
throw MongooseCastErrorToValidationError(e)
|
|
70
|
+
}
|
|
71
|
+
if(e instanceof MongoServerError || e.name === 'MongoServerError'){
|
|
72
|
+
throw MongoServerErrorToValidationError(e)
|
|
40
73
|
}
|
|
41
74
|
throw e
|
|
42
75
|
}
|
|
43
76
|
}
|
|
44
77
|
|
|
45
78
|
async updatePartial(id: string, data: any): Promise<T> {
|
|
79
|
+
|
|
80
|
+
this.assertId(id)
|
|
81
|
+
|
|
46
82
|
try {
|
|
83
|
+
|
|
47
84
|
const item: mongoose.HydratedDocument<T> = await this._model.findOneAndUpdate({_id: id}, data, {new: true}).populate(this._populateFields).exec()
|
|
48
|
-
return item
|
|
85
|
+
return this._lean ? item : item.toObject()
|
|
49
86
|
} catch (e) {
|
|
50
87
|
if (e instanceof mongoose.Error.ValidationError) {
|
|
51
|
-
throw
|
|
88
|
+
throw MongooseValidationErrorToValidationError(e)
|
|
89
|
+
}
|
|
90
|
+
if (e instanceof mongoose.Error.CastError) {
|
|
91
|
+
throw MongooseCastErrorToValidationError(e)
|
|
92
|
+
}
|
|
93
|
+
if(e instanceof MongoServerError || e.name === 'MongoServerError'){
|
|
94
|
+
throw MongoServerErrorToValidationError(e)
|
|
52
95
|
}
|
|
53
96
|
throw e
|
|
54
97
|
}
|
|
55
98
|
}
|
|
56
99
|
|
|
57
100
|
async delete(id: string): Promise<boolean> {
|
|
101
|
+
this.assertId(id)
|
|
58
102
|
const result: DeleteResult = await this._model.deleteOne({_id: id}).exec()
|
|
59
103
|
return result.deletedCount == 1
|
|
60
104
|
}
|
|
61
105
|
|
|
62
106
|
async findById(id: string): Promise<T | null> {
|
|
63
|
-
|
|
64
|
-
|
|
107
|
+
|
|
108
|
+
const item = await this._model
|
|
109
|
+
.findById(id)
|
|
110
|
+
.populate(this._populateFields)
|
|
111
|
+
.lean(this._lean)
|
|
112
|
+
.exec()
|
|
113
|
+
|
|
114
|
+
return item as T;
|
|
65
115
|
}
|
|
66
116
|
|
|
67
117
|
async findByIds(ids: Array<string>): Promise<T[]> {
|
|
68
|
-
|
|
69
|
-
|
|
118
|
+
|
|
119
|
+
const items = await this._model
|
|
120
|
+
.find({_id: {$in: ids}})
|
|
121
|
+
.populate(this._populateFields)
|
|
122
|
+
.lean(this._lean)
|
|
123
|
+
.exec()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
return items as T[]
|
|
70
127
|
}
|
|
71
128
|
|
|
72
129
|
async findOneBy(field: string, value: any): Promise<T | null> {
|
|
73
130
|
const filter: any = {[field]: value}
|
|
74
|
-
|
|
75
|
-
|
|
131
|
+
|
|
132
|
+
const item = await this._model
|
|
133
|
+
.findOne(filter)
|
|
134
|
+
.populate(this._populateFields)
|
|
135
|
+
.lean(this._lean)
|
|
136
|
+
.exec()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
return item as T
|
|
76
140
|
}
|
|
77
141
|
|
|
78
|
-
async findBy(field: string, value: any): Promise<T[]> {
|
|
142
|
+
async findBy(field: string, value: any, limit: number = 0): Promise<T[]> {
|
|
79
143
|
const filter: any = {[field]: value}
|
|
80
|
-
const items
|
|
81
|
-
|
|
144
|
+
const items = await this._model
|
|
145
|
+
.find(filter)
|
|
146
|
+
.limit(limit)
|
|
147
|
+
.populate(this._populateFields)
|
|
148
|
+
.lean(this._lean)
|
|
149
|
+
.exec()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
return items as T[]
|
|
82
153
|
}
|
|
83
154
|
|
|
84
155
|
async fetchAll(): Promise<T[]> {
|
|
85
|
-
|
|
86
|
-
|
|
156
|
+
|
|
157
|
+
const items = await this._model
|
|
158
|
+
.find()
|
|
159
|
+
.populate(this._populateFields)
|
|
160
|
+
.lean(this._lean)
|
|
161
|
+
.exec()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
return items as T[]
|
|
87
165
|
}
|
|
88
166
|
|
|
89
167
|
async search(value: string, limit: number = 1000, filters: IDraxFieldFilter[] =[]): Promise<T[]> {
|
|
90
168
|
|
|
91
169
|
const query = {}
|
|
92
170
|
|
|
93
|
-
if
|
|
171
|
+
if(mongoose.Types.ObjectId.isValid(value)) {
|
|
172
|
+
query['_id'] = new mongoose.Types.ObjectId(value)
|
|
173
|
+
}else if (value) {
|
|
94
174
|
query['$or'] = this._searchFields.map(field => ({[field]: new RegExp(value.toString(), 'i')}))
|
|
95
175
|
}
|
|
96
176
|
|
|
97
177
|
MongooseQueryFilter.applyFilters(query, filters)
|
|
98
178
|
|
|
99
|
-
const items
|
|
100
|
-
|
|
179
|
+
const items = await this._model
|
|
180
|
+
.find(query)
|
|
181
|
+
.limit(limit)
|
|
182
|
+
.populate(this._populateFields)
|
|
183
|
+
.lean(this._lean)
|
|
184
|
+
.exec()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
return items as T[]
|
|
101
188
|
}
|
|
102
189
|
|
|
103
190
|
|
|
@@ -105,22 +192,27 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
105
192
|
page = 1,
|
|
106
193
|
limit = 5,
|
|
107
194
|
orderBy = '',
|
|
108
|
-
order =
|
|
195
|
+
order = "asc",
|
|
109
196
|
search = '',
|
|
110
197
|
filters = []
|
|
111
198
|
}: IDraxPaginateOptions): Promise<IDraxPaginateResult<T>> {
|
|
112
199
|
|
|
113
200
|
const query = {}
|
|
114
201
|
|
|
115
|
-
if
|
|
116
|
-
|
|
202
|
+
if(search){
|
|
203
|
+
if(mongoose.Types.ObjectId.isValid(search)) {
|
|
204
|
+
query['_id'] = new mongoose.Types.ObjectId(search)
|
|
205
|
+
}else{
|
|
206
|
+
query['$or'] = this._searchFields.map(field => ({[field]: new RegExp(search.toString(), 'i')}))
|
|
207
|
+
}
|
|
117
208
|
}
|
|
118
209
|
|
|
119
210
|
MongooseQueryFilter.applyFilters(query, filters)
|
|
120
211
|
|
|
121
212
|
const sort = MongooseSort.applySort(orderBy, order)
|
|
122
213
|
const populate = this._populateFields
|
|
123
|
-
const
|
|
214
|
+
const lean = this._lean
|
|
215
|
+
const options = {page, limit, sort, populate, lean} as PaginateOptions
|
|
124
216
|
const items: PaginateResult<T> = await this._model.paginate(query, options)
|
|
125
217
|
return {
|
|
126
218
|
page: page,
|
|
@@ -137,14 +229,23 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
137
229
|
|
|
138
230
|
const query = {}
|
|
139
231
|
|
|
140
|
-
if
|
|
141
|
-
|
|
232
|
+
if(search){
|
|
233
|
+
if(mongoose.Types.ObjectId.isValid(search)) {
|
|
234
|
+
query['_id'] = new mongoose.Types.ObjectId(search)
|
|
235
|
+
}else{
|
|
236
|
+
query['$or'] = this._searchFields.map(field => ({[field]: new RegExp(search.toString(), 'i')}))
|
|
237
|
+
}
|
|
142
238
|
}
|
|
143
239
|
|
|
144
240
|
MongooseQueryFilter.applyFilters(query, filters)
|
|
145
241
|
|
|
146
|
-
const
|
|
147
|
-
|
|
242
|
+
const item = this._model
|
|
243
|
+
.findOne(query)
|
|
244
|
+
.populate(this._populateFields)
|
|
245
|
+
.lean(this._lean)
|
|
246
|
+
.exec()
|
|
247
|
+
|
|
248
|
+
return item as T
|
|
148
249
|
}
|
|
149
250
|
|
|
150
251
|
async find({
|
|
@@ -157,15 +258,27 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
157
258
|
|
|
158
259
|
const query = {}
|
|
159
260
|
|
|
160
|
-
if
|
|
161
|
-
|
|
261
|
+
if(search){
|
|
262
|
+
if(mongoose.Types.ObjectId.isValid(search)) {
|
|
263
|
+
query['_id'] = new mongoose.Types.ObjectId(search)
|
|
264
|
+
}else{
|
|
265
|
+
query['$or'] = this._searchFields.map(field => ({[field]: new RegExp(search.toString(), 'i')}))
|
|
266
|
+
}
|
|
162
267
|
}
|
|
163
268
|
|
|
164
269
|
MongooseQueryFilter.applyFilters(query, filters)
|
|
165
270
|
|
|
166
271
|
const sort = MongooseSort.applySort(orderBy, order)
|
|
167
|
-
const
|
|
168
|
-
|
|
272
|
+
const items = await this._model
|
|
273
|
+
.find(query)
|
|
274
|
+
.limit(limit)
|
|
275
|
+
.sort(sort)
|
|
276
|
+
.populate(this._populateFields)
|
|
277
|
+
.lean(this._lean)
|
|
278
|
+
.exec()
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
return items as T[]
|
|
169
282
|
}
|
|
170
283
|
|
|
171
284
|
async findCursor({
|
|
@@ -1,59 +1,103 @@
|
|
|
1
1
|
import sqlite from "better-sqlite3";
|
|
2
|
-
import type {IDraxCrud, IDraxPaginateOptions, IDraxPaginateResult} from "@drax/crud-share";
|
|
2
|
+
import type {IDraxCrud, IDraxFindOptions, IDraxPaginateOptions, IDraxPaginateResult} from "@drax/crud-share";
|
|
3
3
|
import {randomUUID} from "node:crypto";
|
|
4
4
|
import {
|
|
5
5
|
SqlSort, SqlQueryFilter, SqliteTableBuilder, SqliteTableField,
|
|
6
|
-
SqliteErrorToValidationError
|
|
6
|
+
SqliteErrorToValidationError
|
|
7
|
+
} from "@drax/common-back";
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
class AbstractSqliteRepository<T> implements IDraxCrud<T, T, T>{
|
|
10
|
+
class AbstractSqliteRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
11
11
|
protected db: any;
|
|
12
|
-
protected
|
|
13
|
-
protected
|
|
12
|
+
protected dataBaseFile: string;
|
|
13
|
+
protected tableName: string = '';
|
|
14
14
|
protected searchFields: string[] = [];
|
|
15
15
|
protected booleanFields: string[] = [];
|
|
16
|
-
protected identifier: string = '
|
|
17
|
-
protected
|
|
16
|
+
protected identifier: string = '_id';
|
|
17
|
+
protected populateFields: { field: string, table: string, identifier: string }[]
|
|
18
18
|
protected tableFields: SqliteTableField[];
|
|
19
|
+
protected verbose: boolean;
|
|
19
20
|
|
|
20
|
-
constructor(
|
|
21
|
-
if(!
|
|
22
|
-
throw new Error("
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if(!tableName){
|
|
26
|
-
throw new Error("tableName is required")
|
|
21
|
+
constructor(dataBaseFile: string, verbose: boolean = false) {
|
|
22
|
+
if (!dataBaseFile) {
|
|
23
|
+
throw new Error("dataBaseFile is required")
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
this.dataBase = dataBase;
|
|
31
|
-
this.tableName = tableName
|
|
32
|
-
this.identifier = identifier
|
|
33
|
-
this.searchFields = searchFields
|
|
34
|
-
this.booleanFields = booleanFields
|
|
26
|
+
this.dataBaseFile = dataBaseFile;
|
|
35
27
|
this.verbose = verbose;
|
|
36
|
-
this.db = new sqlite(
|
|
28
|
+
this.db = new sqlite(dataBaseFile, {verbose: verbose ? console.log : null});
|
|
37
29
|
}
|
|
38
30
|
|
|
39
31
|
build() {
|
|
40
|
-
const builder = new SqliteTableBuilder(this.
|
|
32
|
+
const builder = new SqliteTableBuilder(this.dataBaseFile, this.tableName, this.tableFields, this.verbose);
|
|
41
33
|
builder.build(this.identifier)
|
|
42
34
|
}
|
|
43
35
|
|
|
36
|
+
hasCreatedAt() {
|
|
37
|
+
return this.tableFields.some(field => field.name === 'createdAt')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
hasUpdatedAt() {
|
|
41
|
+
return this.tableFields.some(field => field.name === 'updatedAt')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async prepareData(data: any){
|
|
45
|
+
return data
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async prepareItem(item: any) {
|
|
49
|
+
return item
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async execPopulate(item: any) {
|
|
53
|
+
for (const field of this.populateFields) {
|
|
54
|
+
if (item[field.field]) {
|
|
55
|
+
item[field.field] = this.db.prepare(`SELECT *
|
|
56
|
+
FROM ${field.table}
|
|
57
|
+
WHERE ${field.identifier} = ?`).get(item[field.field]);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return item
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
castToBoolean(item: any) {
|
|
64
|
+
for (const field of this.booleanFields) {
|
|
65
|
+
if(item[field] != undefined){
|
|
66
|
+
item[field] = item[field] === 1 || item[field] === 'true'
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async decorate(item: any) {
|
|
73
|
+
await this.execPopulate(item)
|
|
74
|
+
this.castToBoolean(item)
|
|
75
|
+
await this.prepareItem(item)
|
|
76
|
+
}
|
|
77
|
+
|
|
44
78
|
async create(data: any): Promise<T> {
|
|
45
|
-
try{
|
|
79
|
+
try {
|
|
46
80
|
|
|
47
|
-
if(!data[this.identifier]){
|
|
81
|
+
if (!data[this.identifier]) {
|
|
48
82
|
data[this.identifier] = randomUUID()
|
|
49
83
|
}
|
|
50
84
|
|
|
51
|
-
for(const key in data){
|
|
52
|
-
if(typeof data[key] === 'boolean'){
|
|
53
|
-
data[key] = data[key]? 1 : 0
|
|
85
|
+
for (const key in data) {
|
|
86
|
+
if (typeof data[key] === 'boolean') {
|
|
87
|
+
data[key] = data[key] ? 1 : 0
|
|
54
88
|
}
|
|
55
89
|
}
|
|
56
90
|
|
|
91
|
+
if (this.hasCreatedAt()) {
|
|
92
|
+
data.createdAt = (new Date().toISOString())
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (this.hasUpdatedAt()) {
|
|
96
|
+
data.updatedAt = (new Date().toISOString())
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await this.prepareData(data)
|
|
100
|
+
|
|
57
101
|
const fields = Object.keys(data)
|
|
58
102
|
.map(field => `${field}`)
|
|
59
103
|
.join(', ');
|
|
@@ -62,47 +106,53 @@ class AbstractSqliteRepository<T> implements IDraxCrud<T, T, T>{
|
|
|
62
106
|
.map(field => `@${field}`)
|
|
63
107
|
.join(', ');
|
|
64
108
|
|
|
65
|
-
const stmt = this.db.prepare(`INSERT INTO ${this.tableName} (${fields})
|
|
109
|
+
const stmt = this.db.prepare(`INSERT INTO ${this.tableName} (${fields})
|
|
110
|
+
VALUES (${values})`);
|
|
66
111
|
stmt.run(data)
|
|
67
|
-
|
|
68
|
-
|
|
112
|
+
|
|
113
|
+
const item = await this.findById(data[this.identifier])
|
|
114
|
+
return item
|
|
115
|
+
|
|
116
|
+
} catch (e) {
|
|
69
117
|
console.log(e)
|
|
70
118
|
throw SqliteErrorToValidationError(e, data)
|
|
71
119
|
}
|
|
72
120
|
}
|
|
73
121
|
|
|
74
|
-
async
|
|
75
|
-
|
|
76
|
-
return item
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async findBy(field: string, value: any): Promise<T[] | null>{
|
|
80
|
-
const item = this.db.prepare(`SELECT * FROM ${this.tableName} WHERE ${field} = ?`).all(value);
|
|
81
|
-
return item
|
|
122
|
+
async updatePartial(id: string, data: any): Promise<T> {
|
|
123
|
+
return await this.update(id, data)
|
|
82
124
|
}
|
|
83
125
|
|
|
84
|
-
async findOneBy(field: string, value: any): Promise<T | null>{
|
|
85
|
-
const item = this.db.prepare(`SELECT * FROM ${this.tableName} WHERE ${field} = ?`).get(value);
|
|
86
|
-
return item
|
|
87
|
-
}
|
|
88
126
|
|
|
89
127
|
async update(id: string, data: any): Promise<T> {
|
|
90
|
-
try{
|
|
128
|
+
try {
|
|
91
129
|
|
|
92
|
-
for(const key in data){
|
|
93
|
-
if(typeof data[key] === 'boolean'){
|
|
94
|
-
data[key] = data[key]? 1 : 0
|
|
130
|
+
for (const key in data) {
|
|
131
|
+
if (typeof data[key] === 'boolean') {
|
|
132
|
+
data[key] = data[key] ? 1 : 0
|
|
95
133
|
}
|
|
96
134
|
}
|
|
97
135
|
|
|
136
|
+
if (this.hasUpdatedAt()) {
|
|
137
|
+
data.updatedAt = (new Date().toISOString())
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await this.prepareData(data)
|
|
141
|
+
|
|
98
142
|
const setClauses = Object.keys(data)
|
|
99
143
|
.map(field => `${field} = @${field}`)
|
|
100
144
|
.join(', ');
|
|
145
|
+
|
|
101
146
|
data.identifier = id
|
|
102
|
-
|
|
147
|
+
|
|
148
|
+
const stmt = this.db.prepare(`UPDATE ${this.tableName}
|
|
149
|
+
SET ${setClauses}
|
|
150
|
+
WHERE ${this.identifier} = @identifier `);
|
|
103
151
|
stmt.run(data);
|
|
104
|
-
|
|
105
|
-
|
|
152
|
+
const item = await this.findById(id)
|
|
153
|
+
return item
|
|
154
|
+
|
|
155
|
+
} catch (e) {
|
|
106
156
|
console.log(e)
|
|
107
157
|
throw SqliteErrorToValidationError(e, data)
|
|
108
158
|
}
|
|
@@ -111,24 +161,33 @@ class AbstractSqliteRepository<T> implements IDraxCrud<T, T, T>{
|
|
|
111
161
|
|
|
112
162
|
|
|
113
163
|
async delete(id: string): Promise<boolean> {
|
|
114
|
-
const stmt = this.db.prepare(`DELETE
|
|
164
|
+
const stmt = this.db.prepare(`DELETE
|
|
165
|
+
FROM ${this.tableName}
|
|
166
|
+
WHERE ${this.identifier} = ?`);
|
|
115
167
|
stmt.run(id);
|
|
116
168
|
return true
|
|
117
169
|
}
|
|
118
170
|
|
|
171
|
+
async deleteAll(): Promise<boolean> {
|
|
172
|
+
const stmt = this.db.prepare(`DELETE
|
|
173
|
+
FROM ${this.tableName}`);
|
|
174
|
+
stmt.run();
|
|
175
|
+
return true
|
|
176
|
+
}
|
|
119
177
|
|
|
120
178
|
|
|
121
179
|
async paginate({
|
|
122
|
-
page= 1,
|
|
123
|
-
limit= 5,
|
|
124
|
-
orderBy= '',
|
|
125
|
-
order= 'desc',
|
|
126
|
-
search= '',
|
|
127
|
-
filters= []
|
|
180
|
+
page = 1,
|
|
181
|
+
limit = 5,
|
|
182
|
+
orderBy = '',
|
|
183
|
+
order = 'desc',
|
|
184
|
+
search = '',
|
|
185
|
+
filters = []
|
|
186
|
+
}: IDraxPaginateOptions): Promise<IDraxPaginateResult<T>> {
|
|
128
187
|
|
|
129
188
|
const offset = page > 1 ? (page - 1) * limit : 0
|
|
130
189
|
|
|
131
|
-
let where=""
|
|
190
|
+
let where = ""
|
|
132
191
|
if (search && this.searchFields.length > 0) {
|
|
133
192
|
where = ` WHERE ${this.searchFields.map(field => `${field} LIKE '%${search}%'`).join(" OR ")}`
|
|
134
193
|
}
|
|
@@ -139,16 +198,14 @@ class AbstractSqliteRepository<T> implements IDraxCrud<T, T, T>{
|
|
|
139
198
|
|
|
140
199
|
const sort = SqlSort.applySort(orderBy, order)
|
|
141
200
|
|
|
142
|
-
const rCount = this.db.prepare(`SELECT COUNT(*) as count
|
|
143
|
-
|
|
201
|
+
const rCount = this.db.prepare(`SELECT COUNT(*) as count
|
|
202
|
+
FROM ${this.tableName} ${where}`).get();
|
|
203
|
+
const items = this.db.prepare(`SELECT *
|
|
204
|
+
FROM ${this.tableName} ${where} ${sort} LIMIT ?
|
|
205
|
+
OFFSET ? `).all([limit, offset]) as T[];
|
|
144
206
|
|
|
145
|
-
for(const item of items){
|
|
146
|
-
|
|
147
|
-
if (this.booleanFields.includes(key)) {
|
|
148
|
-
//@ts-ignore
|
|
149
|
-
item[key] = item[key] ? true : false
|
|
150
|
-
}
|
|
151
|
-
}
|
|
207
|
+
for (const item of items) {
|
|
208
|
+
await this.decorate(item)
|
|
152
209
|
}
|
|
153
210
|
|
|
154
211
|
return {
|
|
@@ -159,20 +216,87 @@ class AbstractSqliteRepository<T> implements IDraxCrud<T, T, T>{
|
|
|
159
216
|
}
|
|
160
217
|
}
|
|
161
218
|
|
|
162
|
-
async
|
|
163
|
-
|
|
219
|
+
async find({
|
|
220
|
+
limit = 5,
|
|
221
|
+
orderBy = '',
|
|
222
|
+
order = 'desc',
|
|
223
|
+
search = '',
|
|
224
|
+
filters = []
|
|
225
|
+
}: IDraxFindOptions): Promise<any[]> {
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
let where = ""
|
|
229
|
+
if (search && this.searchFields.length > 0) {
|
|
230
|
+
where = ` WHERE ${this.searchFields.map(field => `${field} LIKE '%${search}%'`).join(" OR ")}`
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (filters.length > 0) {
|
|
234
|
+
where = SqlQueryFilter.applyFilters(where, filters)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const sort = SqlSort.applySort(orderBy, order)
|
|
238
|
+
|
|
239
|
+
const rCount = this.db.prepare(`SELECT COUNT(*) as count
|
|
240
|
+
FROM ${this.tableName} ${where}`).get();
|
|
241
|
+
const items = this.db.prepare(`SELECT *
|
|
242
|
+
FROM ${this.tableName} ${where} ${sort} LIMIT ? `).all([limit]) as T[];
|
|
243
|
+
|
|
244
|
+
for (const item of items) {
|
|
245
|
+
await this.decorate(item)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return items
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async fetchAll(): Promise<any[]> {
|
|
252
|
+
const items = this.db.prepare(`SELECT *
|
|
253
|
+
FROM ${this.tableName}`).all();
|
|
254
|
+
for (const item of items) {
|
|
255
|
+
await this.decorate(item)
|
|
256
|
+
}
|
|
164
257
|
return items
|
|
165
258
|
}
|
|
166
259
|
|
|
167
|
-
async search(value: any, limit: number = 1000): Promise<any[]>{
|
|
168
|
-
let where=""
|
|
260
|
+
async search(value: any, limit: number = 1000): Promise<any[]> {
|
|
261
|
+
let where = ""
|
|
169
262
|
if (value && this.searchFields.length > 0) {
|
|
170
263
|
where = ` WHERE ${this.searchFields.map(field => `${field} LIKE '%${value}%'`).join(" OR ")}`
|
|
171
264
|
}
|
|
172
|
-
const items = this.db.prepare(`SELECT *
|
|
265
|
+
const items = this.db.prepare(`SELECT *
|
|
266
|
+
FROM ${this.tableName} ${where} LIMIT ${limit}`).all();
|
|
267
|
+
|
|
268
|
+
for (const item of items) {
|
|
269
|
+
await this.decorate(item)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return items
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async findById(id: string): Promise<T | null> {
|
|
276
|
+
const item = this.db.prepare(`SELECT *
|
|
277
|
+
FROM ${this.tableName}
|
|
278
|
+
WHERE ${this.identifier} = ?`).get(id);
|
|
279
|
+
await this.decorate(item)
|
|
280
|
+
return item
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async findBy(field: string, value: any, limit: number = 0): Promise<T[] | null> {
|
|
284
|
+
const items = this.db.prepare(`SELECT *
|
|
285
|
+
FROM ${this.tableName}
|
|
286
|
+
WHERE ${field} = ? LIMIT ${limit}`).all(value);
|
|
287
|
+
for (const item of items) {
|
|
288
|
+
await this.decorate(item)
|
|
289
|
+
}
|
|
173
290
|
return items
|
|
174
291
|
}
|
|
175
292
|
|
|
293
|
+
async findOneBy(field: string, value: any): Promise<T | null> {
|
|
294
|
+
const item = this.db.prepare(`SELECT *
|
|
295
|
+
FROM ${this.tableName}
|
|
296
|
+
WHERE ${field} = ?`).get(value);
|
|
297
|
+
await this.decorate(item)
|
|
298
|
+
return item
|
|
299
|
+
}
|
|
176
300
|
}
|
|
177
301
|
|
|
178
302
|
export default AbstractSqliteRepository
|