@drax/crud-back 0.11.4 → 0.12.1
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,94 +1,160 @@
|
|
|
1
1
|
import "mongoose-paginate-v2";
|
|
2
2
|
import mongoose from "mongoose";
|
|
3
|
-
import { MongooseQueryFilter, MongooseSort,
|
|
3
|
+
import { MongooseQueryFilter, MongooseSort, MongooseValidationErrorToValidationError, MongooseCastErrorToValidationError, MongoServerErrorToValidationError } from "@drax/common-back";
|
|
4
|
+
import { InvalidIdError } from "@drax/common-back";
|
|
5
|
+
import { MongoServerError } from "mongodb";
|
|
4
6
|
class AbstractMongoRepository {
|
|
5
7
|
constructor() {
|
|
6
8
|
this._searchFields = [];
|
|
7
9
|
this._populateFields = [];
|
|
10
|
+
this._lean = true;
|
|
11
|
+
}
|
|
12
|
+
assertId(id) {
|
|
13
|
+
if (!mongoose.Types.ObjectId.isValid(id)) {
|
|
14
|
+
console.log(`Invalid ID: ${id} is not a valid ObjectId.`);
|
|
15
|
+
throw new InvalidIdError(id);
|
|
16
|
+
}
|
|
8
17
|
}
|
|
9
18
|
async create(data) {
|
|
10
19
|
try {
|
|
11
|
-
const item =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
const item = await this._model.create(data);
|
|
21
|
+
if (this._populateFields && this._populateFields.length > 0) {
|
|
22
|
+
//@ts-ignore
|
|
23
|
+
await item.populate(this._populateFields);
|
|
24
|
+
}
|
|
25
|
+
return this._lean ? item : item.toObject();
|
|
16
26
|
}
|
|
17
27
|
catch (e) {
|
|
18
28
|
if (e instanceof mongoose.Error.ValidationError) {
|
|
19
|
-
throw
|
|
29
|
+
throw MongooseValidationErrorToValidationError(e);
|
|
30
|
+
}
|
|
31
|
+
if (e instanceof mongoose.Error.CastError) {
|
|
32
|
+
throw MongooseCastErrorToValidationError(e);
|
|
33
|
+
}
|
|
34
|
+
if (e instanceof MongoServerError || e.name === 'MongoServerError') {
|
|
35
|
+
throw MongoServerErrorToValidationError(e);
|
|
20
36
|
}
|
|
21
37
|
throw e;
|
|
22
38
|
}
|
|
23
39
|
}
|
|
24
40
|
async update(id, data) {
|
|
41
|
+
this.assertId(id);
|
|
25
42
|
try {
|
|
26
43
|
const item = await this._model.findOneAndUpdate({ _id: id }, data, { new: true }).populate(this._populateFields).exec();
|
|
27
|
-
return item;
|
|
44
|
+
return this._lean ? item : item.toObject();
|
|
28
45
|
}
|
|
29
46
|
catch (e) {
|
|
30
47
|
if (e instanceof mongoose.Error.ValidationError) {
|
|
31
|
-
throw
|
|
48
|
+
throw MongooseValidationErrorToValidationError(e);
|
|
49
|
+
}
|
|
50
|
+
if (e instanceof mongoose.Error.CastError) {
|
|
51
|
+
throw MongooseCastErrorToValidationError(e);
|
|
52
|
+
}
|
|
53
|
+
if (e instanceof MongoServerError || e.name === 'MongoServerError') {
|
|
54
|
+
throw MongoServerErrorToValidationError(e);
|
|
32
55
|
}
|
|
33
56
|
throw e;
|
|
34
57
|
}
|
|
35
58
|
}
|
|
36
59
|
async updatePartial(id, data) {
|
|
60
|
+
this.assertId(id);
|
|
37
61
|
try {
|
|
38
62
|
const item = await this._model.findOneAndUpdate({ _id: id }, data, { new: true }).populate(this._populateFields).exec();
|
|
39
|
-
return item;
|
|
63
|
+
return this._lean ? item : item.toObject();
|
|
40
64
|
}
|
|
41
65
|
catch (e) {
|
|
42
66
|
if (e instanceof mongoose.Error.ValidationError) {
|
|
43
|
-
throw
|
|
67
|
+
throw MongooseValidationErrorToValidationError(e);
|
|
68
|
+
}
|
|
69
|
+
if (e instanceof mongoose.Error.CastError) {
|
|
70
|
+
throw MongooseCastErrorToValidationError(e);
|
|
71
|
+
}
|
|
72
|
+
if (e instanceof MongoServerError || e.name === 'MongoServerError') {
|
|
73
|
+
throw MongoServerErrorToValidationError(e);
|
|
44
74
|
}
|
|
45
75
|
throw e;
|
|
46
76
|
}
|
|
47
77
|
}
|
|
48
78
|
async delete(id) {
|
|
79
|
+
this.assertId(id);
|
|
49
80
|
const result = await this._model.deleteOne({ _id: id }).exec();
|
|
50
81
|
return result.deletedCount == 1;
|
|
51
82
|
}
|
|
52
83
|
async findById(id) {
|
|
53
|
-
const item = await this._model
|
|
84
|
+
const item = await this._model
|
|
85
|
+
.findById(id)
|
|
86
|
+
.populate(this._populateFields)
|
|
87
|
+
.lean(this._lean)
|
|
88
|
+
.exec();
|
|
54
89
|
return item;
|
|
55
90
|
}
|
|
56
91
|
async findByIds(ids) {
|
|
57
|
-
const items = await this._model
|
|
92
|
+
const items = await this._model
|
|
93
|
+
.find({ _id: { $in: ids } })
|
|
94
|
+
.populate(this._populateFields)
|
|
95
|
+
.lean(this._lean)
|
|
96
|
+
.exec();
|
|
58
97
|
return items;
|
|
59
98
|
}
|
|
60
99
|
async findOneBy(field, value) {
|
|
61
100
|
const filter = { [field]: value };
|
|
62
|
-
const item = await this._model
|
|
101
|
+
const item = await this._model
|
|
102
|
+
.findOne(filter)
|
|
103
|
+
.populate(this._populateFields)
|
|
104
|
+
.lean(this._lean)
|
|
105
|
+
.exec();
|
|
63
106
|
return item;
|
|
64
107
|
}
|
|
65
|
-
async findBy(field, value) {
|
|
108
|
+
async findBy(field, value, limit = 0) {
|
|
66
109
|
const filter = { [field]: value };
|
|
67
|
-
const items = await this._model
|
|
110
|
+
const items = await this._model
|
|
111
|
+
.find(filter)
|
|
112
|
+
.limit(limit)
|
|
113
|
+
.populate(this._populateFields)
|
|
114
|
+
.lean(this._lean)
|
|
115
|
+
.exec();
|
|
68
116
|
return items;
|
|
69
117
|
}
|
|
70
118
|
async fetchAll() {
|
|
71
|
-
const items = await this._model
|
|
119
|
+
const items = await this._model
|
|
120
|
+
.find()
|
|
121
|
+
.populate(this._populateFields)
|
|
122
|
+
.lean(this._lean)
|
|
123
|
+
.exec();
|
|
72
124
|
return items;
|
|
73
125
|
}
|
|
74
126
|
async search(value, limit = 1000, filters = []) {
|
|
75
127
|
const query = {};
|
|
76
|
-
if (value) {
|
|
128
|
+
if (mongoose.Types.ObjectId.isValid(value)) {
|
|
129
|
+
query['_id'] = new mongoose.Types.ObjectId(value);
|
|
130
|
+
}
|
|
131
|
+
else if (value) {
|
|
77
132
|
query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(value.toString(), 'i') }));
|
|
78
133
|
}
|
|
79
134
|
MongooseQueryFilter.applyFilters(query, filters);
|
|
80
|
-
const items = await this._model
|
|
135
|
+
const items = await this._model
|
|
136
|
+
.find(query)
|
|
137
|
+
.limit(limit)
|
|
138
|
+
.populate(this._populateFields)
|
|
139
|
+
.lean(this._lean)
|
|
140
|
+
.exec();
|
|
81
141
|
return items;
|
|
82
142
|
}
|
|
83
|
-
async paginate({ page = 1, limit = 5, orderBy = '', order =
|
|
143
|
+
async paginate({ page = 1, limit = 5, orderBy = '', order = "asc", search = '', filters = [] }) {
|
|
84
144
|
const query = {};
|
|
85
145
|
if (search) {
|
|
86
|
-
|
|
146
|
+
if (mongoose.Types.ObjectId.isValid(search)) {
|
|
147
|
+
query['_id'] = new mongoose.Types.ObjectId(search);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(search.toString(), 'i') }));
|
|
151
|
+
}
|
|
87
152
|
}
|
|
88
153
|
MongooseQueryFilter.applyFilters(query, filters);
|
|
89
154
|
const sort = MongooseSort.applySort(orderBy, order);
|
|
90
155
|
const populate = this._populateFields;
|
|
91
|
-
const
|
|
156
|
+
const lean = this._lean;
|
|
157
|
+
const options = { page, limit, sort, populate, lean };
|
|
92
158
|
const items = await this._model.paginate(query, options);
|
|
93
159
|
return {
|
|
94
160
|
page: page,
|
|
@@ -100,21 +166,41 @@ class AbstractMongoRepository {
|
|
|
100
166
|
async findOne({ search = '', filters = [] }) {
|
|
101
167
|
const query = {};
|
|
102
168
|
if (search) {
|
|
103
|
-
|
|
169
|
+
if (mongoose.Types.ObjectId.isValid(search)) {
|
|
170
|
+
query['_id'] = new mongoose.Types.ObjectId(search);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(search.toString(), 'i') }));
|
|
174
|
+
}
|
|
104
175
|
}
|
|
105
176
|
MongooseQueryFilter.applyFilters(query, filters);
|
|
106
|
-
const
|
|
107
|
-
|
|
177
|
+
const item = this._model
|
|
178
|
+
.findOne(query)
|
|
179
|
+
.populate(this._populateFields)
|
|
180
|
+
.lean(this._lean)
|
|
181
|
+
.exec();
|
|
182
|
+
return item;
|
|
108
183
|
}
|
|
109
184
|
async find({ limit = 0, orderBy = '', order = false, search = '', filters = [] }) {
|
|
110
185
|
const query = {};
|
|
111
186
|
if (search) {
|
|
112
|
-
|
|
187
|
+
if (mongoose.Types.ObjectId.isValid(search)) {
|
|
188
|
+
query['_id'] = new mongoose.Types.ObjectId(search);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(search.toString(), 'i') }));
|
|
192
|
+
}
|
|
113
193
|
}
|
|
114
194
|
MongooseQueryFilter.applyFilters(query, filters);
|
|
115
195
|
const sort = MongooseSort.applySort(orderBy, order);
|
|
116
|
-
const
|
|
117
|
-
|
|
196
|
+
const items = await this._model
|
|
197
|
+
.find(query)
|
|
198
|
+
.limit(limit)
|
|
199
|
+
.sort(sort)
|
|
200
|
+
.populate(this._populateFields)
|
|
201
|
+
.lean(this._lean)
|
|
202
|
+
.exec();
|
|
203
|
+
return items;
|
|
118
204
|
}
|
|
119
205
|
async findCursor({ limit = 0, orderBy = '', order = false, search = '', filters = [] }) {
|
|
120
206
|
const query = {};
|
|
@@ -2,28 +2,56 @@ import sqlite from "better-sqlite3";
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { SqlSort, SqlQueryFilter, SqliteTableBuilder, SqliteErrorToValidationError } from "@drax/common-back";
|
|
4
4
|
class AbstractSqliteRepository {
|
|
5
|
-
constructor(
|
|
5
|
+
constructor(dataBaseFile, verbose = false) {
|
|
6
|
+
this.tableName = '';
|
|
6
7
|
this.searchFields = [];
|
|
7
8
|
this.booleanFields = [];
|
|
8
|
-
this.identifier = '
|
|
9
|
-
if (!
|
|
10
|
-
throw new Error("
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
throw new Error("tableName is required");
|
|
14
|
-
}
|
|
15
|
-
this.dataBase = dataBase;
|
|
16
|
-
this.tableName = tableName;
|
|
17
|
-
this.identifier = identifier;
|
|
18
|
-
this.searchFields = searchFields;
|
|
19
|
-
this.booleanFields = booleanFields;
|
|
9
|
+
this.identifier = '_id';
|
|
10
|
+
if (!dataBaseFile) {
|
|
11
|
+
throw new Error("dataBaseFile is required");
|
|
12
|
+
}
|
|
13
|
+
this.dataBaseFile = dataBaseFile;
|
|
20
14
|
this.verbose = verbose;
|
|
21
|
-
this.db = new sqlite(
|
|
15
|
+
this.db = new sqlite(dataBaseFile, { verbose: verbose ? console.log : null });
|
|
22
16
|
}
|
|
23
17
|
build() {
|
|
24
|
-
const builder = new SqliteTableBuilder(this.
|
|
18
|
+
const builder = new SqliteTableBuilder(this.dataBaseFile, this.tableName, this.tableFields, this.verbose);
|
|
25
19
|
builder.build(this.identifier);
|
|
26
20
|
}
|
|
21
|
+
hasCreatedAt() {
|
|
22
|
+
return this.tableFields.some(field => field.name === 'createdAt');
|
|
23
|
+
}
|
|
24
|
+
hasUpdatedAt() {
|
|
25
|
+
return this.tableFields.some(field => field.name === 'updatedAt');
|
|
26
|
+
}
|
|
27
|
+
async prepareData(data) {
|
|
28
|
+
return data;
|
|
29
|
+
}
|
|
30
|
+
async prepareItem(item) {
|
|
31
|
+
return item;
|
|
32
|
+
}
|
|
33
|
+
async execPopulate(item) {
|
|
34
|
+
for (const field of this.populateFields) {
|
|
35
|
+
if (item[field.field]) {
|
|
36
|
+
item[field.field] = this.db.prepare(`SELECT *
|
|
37
|
+
FROM ${field.table}
|
|
38
|
+
WHERE ${field.identifier} = ?`).get(item[field.field]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return item;
|
|
42
|
+
}
|
|
43
|
+
castToBoolean(item) {
|
|
44
|
+
for (const field of this.booleanFields) {
|
|
45
|
+
if (item[field] != undefined) {
|
|
46
|
+
item[field] = item[field] === 1 || item[field] === 'true';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async decorate(item) {
|
|
51
|
+
await this.execPopulate(item);
|
|
52
|
+
this.castToBoolean(item);
|
|
53
|
+
await this.prepareItem(item);
|
|
54
|
+
}
|
|
27
55
|
async create(data) {
|
|
28
56
|
try {
|
|
29
57
|
if (!data[this.identifier]) {
|
|
@@ -34,32 +62,32 @@ class AbstractSqliteRepository {
|
|
|
34
62
|
data[key] = data[key] ? 1 : 0;
|
|
35
63
|
}
|
|
36
64
|
}
|
|
65
|
+
if (this.hasCreatedAt()) {
|
|
66
|
+
data.createdAt = (new Date().toISOString());
|
|
67
|
+
}
|
|
68
|
+
if (this.hasUpdatedAt()) {
|
|
69
|
+
data.updatedAt = (new Date().toISOString());
|
|
70
|
+
}
|
|
71
|
+
await this.prepareData(data);
|
|
37
72
|
const fields = Object.keys(data)
|
|
38
73
|
.map(field => `${field}`)
|
|
39
74
|
.join(', ');
|
|
40
75
|
const values = Object.keys(data)
|
|
41
76
|
.map(field => `@${field}`)
|
|
42
77
|
.join(', ');
|
|
43
|
-
const stmt = this.db.prepare(`INSERT INTO ${this.tableName} (${fields})
|
|
78
|
+
const stmt = this.db.prepare(`INSERT INTO ${this.tableName} (${fields})
|
|
79
|
+
VALUES (${values})`);
|
|
44
80
|
stmt.run(data);
|
|
45
|
-
|
|
81
|
+
const item = await this.findById(data[this.identifier]);
|
|
82
|
+
return item;
|
|
46
83
|
}
|
|
47
84
|
catch (e) {
|
|
48
85
|
console.log(e);
|
|
49
86
|
throw SqliteErrorToValidationError(e, data);
|
|
50
87
|
}
|
|
51
88
|
}
|
|
52
|
-
async
|
|
53
|
-
|
|
54
|
-
return item;
|
|
55
|
-
}
|
|
56
|
-
async findBy(field, value) {
|
|
57
|
-
const item = this.db.prepare(`SELECT * FROM ${this.tableName} WHERE ${field} = ?`).all(value);
|
|
58
|
-
return item;
|
|
59
|
-
}
|
|
60
|
-
async findOneBy(field, value) {
|
|
61
|
-
const item = this.db.prepare(`SELECT * FROM ${this.tableName} WHERE ${field} = ?`).get(value);
|
|
62
|
-
return item;
|
|
89
|
+
async updatePartial(id, data) {
|
|
90
|
+
return await this.update(id, data);
|
|
63
91
|
}
|
|
64
92
|
async update(id, data) {
|
|
65
93
|
try {
|
|
@@ -68,13 +96,20 @@ class AbstractSqliteRepository {
|
|
|
68
96
|
data[key] = data[key] ? 1 : 0;
|
|
69
97
|
}
|
|
70
98
|
}
|
|
99
|
+
if (this.hasUpdatedAt()) {
|
|
100
|
+
data.updatedAt = (new Date().toISOString());
|
|
101
|
+
}
|
|
102
|
+
await this.prepareData(data);
|
|
71
103
|
const setClauses = Object.keys(data)
|
|
72
104
|
.map(field => `${field} = @${field}`)
|
|
73
105
|
.join(', ');
|
|
74
106
|
data.identifier = id;
|
|
75
|
-
const stmt = this.db.prepare(`UPDATE ${this.tableName}
|
|
107
|
+
const stmt = this.db.prepare(`UPDATE ${this.tableName}
|
|
108
|
+
SET ${setClauses}
|
|
109
|
+
WHERE ${this.identifier} = @identifier `);
|
|
76
110
|
stmt.run(data);
|
|
77
|
-
|
|
111
|
+
const item = await this.findById(id);
|
|
112
|
+
return item;
|
|
78
113
|
}
|
|
79
114
|
catch (e) {
|
|
80
115
|
console.log(e);
|
|
@@ -82,10 +117,18 @@ class AbstractSqliteRepository {
|
|
|
82
117
|
}
|
|
83
118
|
}
|
|
84
119
|
async delete(id) {
|
|
85
|
-
const stmt = this.db.prepare(`DELETE
|
|
120
|
+
const stmt = this.db.prepare(`DELETE
|
|
121
|
+
FROM ${this.tableName}
|
|
122
|
+
WHERE ${this.identifier} = ?`);
|
|
86
123
|
stmt.run(id);
|
|
87
124
|
return true;
|
|
88
125
|
}
|
|
126
|
+
async deleteAll() {
|
|
127
|
+
const stmt = this.db.prepare(`DELETE
|
|
128
|
+
FROM ${this.tableName}`);
|
|
129
|
+
stmt.run();
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
89
132
|
async paginate({ page = 1, limit = 5, orderBy = '', order = 'desc', search = '', filters = [] }) {
|
|
90
133
|
const offset = page > 1 ? (page - 1) * limit : 0;
|
|
91
134
|
let where = "";
|
|
@@ -96,15 +139,13 @@ class AbstractSqliteRepository {
|
|
|
96
139
|
where = SqlQueryFilter.applyFilters(where, filters);
|
|
97
140
|
}
|
|
98
141
|
const sort = SqlSort.applySort(orderBy, order);
|
|
99
|
-
const rCount = this.db.prepare(`SELECT COUNT(*) as count
|
|
100
|
-
|
|
142
|
+
const rCount = this.db.prepare(`SELECT COUNT(*) as count
|
|
143
|
+
FROM ${this.tableName} ${where}`).get();
|
|
144
|
+
const items = this.db.prepare(`SELECT *
|
|
145
|
+
FROM ${this.tableName} ${where} ${sort} LIMIT ?
|
|
146
|
+
OFFSET ? `).all([limit, offset]);
|
|
101
147
|
for (const item of items) {
|
|
102
|
-
|
|
103
|
-
if (this.booleanFields.includes(key)) {
|
|
104
|
-
//@ts-ignore
|
|
105
|
-
item[key] = item[key] ? true : false;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
148
|
+
await this.decorate(item);
|
|
108
149
|
}
|
|
109
150
|
return {
|
|
110
151
|
page: page,
|
|
@@ -113,8 +154,30 @@ class AbstractSqliteRepository {
|
|
|
113
154
|
items: items
|
|
114
155
|
};
|
|
115
156
|
}
|
|
157
|
+
async find({ limit = 5, orderBy = '', order = 'desc', search = '', filters = [] }) {
|
|
158
|
+
let where = "";
|
|
159
|
+
if (search && this.searchFields.length > 0) {
|
|
160
|
+
where = ` WHERE ${this.searchFields.map(field => `${field} LIKE '%${search}%'`).join(" OR ")}`;
|
|
161
|
+
}
|
|
162
|
+
if (filters.length > 0) {
|
|
163
|
+
where = SqlQueryFilter.applyFilters(where, filters);
|
|
164
|
+
}
|
|
165
|
+
const sort = SqlSort.applySort(orderBy, order);
|
|
166
|
+
const rCount = this.db.prepare(`SELECT COUNT(*) as count
|
|
167
|
+
FROM ${this.tableName} ${where}`).get();
|
|
168
|
+
const items = this.db.prepare(`SELECT *
|
|
169
|
+
FROM ${this.tableName} ${where} ${sort} LIMIT ? `).all([limit]);
|
|
170
|
+
for (const item of items) {
|
|
171
|
+
await this.decorate(item);
|
|
172
|
+
}
|
|
173
|
+
return items;
|
|
174
|
+
}
|
|
116
175
|
async fetchAll() {
|
|
117
|
-
const items = this.db.prepare(`SELECT *
|
|
176
|
+
const items = this.db.prepare(`SELECT *
|
|
177
|
+
FROM ${this.tableName}`).all();
|
|
178
|
+
for (const item of items) {
|
|
179
|
+
await this.decorate(item);
|
|
180
|
+
}
|
|
118
181
|
return items;
|
|
119
182
|
}
|
|
120
183
|
async search(value, limit = 1000) {
|
|
@@ -122,8 +185,35 @@ class AbstractSqliteRepository {
|
|
|
122
185
|
if (value && this.searchFields.length > 0) {
|
|
123
186
|
where = ` WHERE ${this.searchFields.map(field => `${field} LIKE '%${value}%'`).join(" OR ")}`;
|
|
124
187
|
}
|
|
125
|
-
const items = this.db.prepare(`SELECT *
|
|
188
|
+
const items = this.db.prepare(`SELECT *
|
|
189
|
+
FROM ${this.tableName} ${where} LIMIT ${limit}`).all();
|
|
190
|
+
for (const item of items) {
|
|
191
|
+
await this.decorate(item);
|
|
192
|
+
}
|
|
126
193
|
return items;
|
|
127
194
|
}
|
|
195
|
+
async findById(id) {
|
|
196
|
+
const item = this.db.prepare(`SELECT *
|
|
197
|
+
FROM ${this.tableName}
|
|
198
|
+
WHERE ${this.identifier} = ?`).get(id);
|
|
199
|
+
await this.decorate(item);
|
|
200
|
+
return item;
|
|
201
|
+
}
|
|
202
|
+
async findBy(field, value, limit = 0) {
|
|
203
|
+
const items = this.db.prepare(`SELECT *
|
|
204
|
+
FROM ${this.tableName}
|
|
205
|
+
WHERE ${field} = ? LIMIT ${limit}`).all(value);
|
|
206
|
+
for (const item of items) {
|
|
207
|
+
await this.decorate(item);
|
|
208
|
+
}
|
|
209
|
+
return items;
|
|
210
|
+
}
|
|
211
|
+
async findOneBy(field, value) {
|
|
212
|
+
const item = this.db.prepare(`SELECT *
|
|
213
|
+
FROM ${this.tableName}
|
|
214
|
+
WHERE ${field} = ?`).get(value);
|
|
215
|
+
await this.decorate(item);
|
|
216
|
+
return item;
|
|
217
|
+
}
|
|
128
218
|
}
|
|
129
219
|
export default AbstractSqliteRepository;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
const ErrorBodyResponseSchema = z.object({
|
|
3
|
+
statusCode: z.string(),
|
|
4
|
+
error: z.string(),
|
|
5
|
+
message: z.string(),
|
|
6
|
+
i18nMessage: z.string()
|
|
7
|
+
});
|
|
8
|
+
const ValidationErrorBodyResponseSchema = ErrorBodyResponseSchema.extend({
|
|
9
|
+
inputErrors: z.array(z.object({
|
|
10
|
+
field: z.string(),
|
|
11
|
+
reason: z.string(),
|
|
12
|
+
value: z.any().optional(),
|
|
13
|
+
})).optional()
|
|
14
|
+
});
|
|
15
|
+
export default ErrorBodyResponseSchema;
|
|
16
|
+
export { ErrorBodyResponseSchema, ValidationErrorBodyResponseSchema };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
import QueryFilterRegex from "../regexs/QueryFilterRegex.js";
|
|
3
|
+
const FindQuerySchema = z.object({
|
|
4
|
+
orderBy: z.string().optional(),
|
|
5
|
+
order: z.enum(["asc", "desc"]).optional(),
|
|
6
|
+
search: z.string().optional(),
|
|
7
|
+
filters: z.string().regex(QueryFilterRegex).optional().describe("Format: field;operator;value|field;operator;value|..."),
|
|
8
|
+
});
|
|
9
|
+
export { FindQuerySchema };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
const PaginateQuerySchema = z.object({
|
|
3
|
+
page: z.number(),
|
|
4
|
+
limit: z.number(),
|
|
5
|
+
orderBy: z.string().optional(),
|
|
6
|
+
order: z.enum(["asc", "desc"]).optional(),
|
|
7
|
+
search: z.string().optional(),
|
|
8
|
+
filters: z.array(z.object({
|
|
9
|
+
field: z.string(),
|
|
10
|
+
operator: z.string(),
|
|
11
|
+
value: z.any(),
|
|
12
|
+
})).optional()
|
|
13
|
+
});
|
|
14
|
+
const PaginateBodyResponseSchema = z.object({
|
|
15
|
+
page: z.number(),
|
|
16
|
+
limit: z.number(),
|
|
17
|
+
total: z.number(),
|
|
18
|
+
items: z.array(z.any())
|
|
19
|
+
});
|
|
20
|
+
export { PaginateQuerySchema, PaginateBodyResponseSchema };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
import QueryFilterRegex from "../regexs/QueryFilterRegex.js";
|
|
3
|
+
const PaginateQuerySchema = z.object({
|
|
4
|
+
page: z.number().optional(),
|
|
5
|
+
limit: z.number().optional(),
|
|
6
|
+
orderBy: z.string().optional(),
|
|
7
|
+
order: z.enum(["asc", "desc"]).optional(),
|
|
8
|
+
search: z.string().optional(),
|
|
9
|
+
filters: z.string().regex(QueryFilterRegex).optional().describe("Format: field;operator;value|field;operator;value|..."),
|
|
10
|
+
});
|
|
11
|
+
const PaginateBodyResponseSchema = z.object({
|
|
12
|
+
page: z.number(),
|
|
13
|
+
limit: z.number(),
|
|
14
|
+
total: z.number(),
|
|
15
|
+
items: z.array(z.any())
|
|
16
|
+
});
|
|
17
|
+
export { PaginateQuerySchema, PaginateBodyResponseSchema };
|
|
@@ -47,6 +47,9 @@ class AbstractService {
|
|
|
47
47
|
}
|
|
48
48
|
async updatePartial(id, data) {
|
|
49
49
|
try {
|
|
50
|
+
if (this._schema) {
|
|
51
|
+
await this._schema.partial().parseAsync(data);
|
|
52
|
+
}
|
|
50
53
|
const item = await this._repository.updatePartial(id, data);
|
|
51
54
|
return item;
|
|
52
55
|
}
|
|
@@ -110,9 +113,9 @@ class AbstractService {
|
|
|
110
113
|
throw e;
|
|
111
114
|
}
|
|
112
115
|
}
|
|
113
|
-
async findBy(field, value) {
|
|
116
|
+
async findBy(field, value, limit = 1000) {
|
|
114
117
|
try {
|
|
115
|
-
let items = await this._repository.findBy(field, value);
|
|
118
|
+
let items = await this._repository.findBy(field, value, limit);
|
|
116
119
|
if (this.transformRead) {
|
|
117
120
|
items = await Promise.all(items.map(item => this.transformRead(item)));
|
|
118
121
|
}
|
|
@@ -149,7 +152,7 @@ class AbstractService {
|
|
|
149
152
|
throw e;
|
|
150
153
|
}
|
|
151
154
|
}
|
|
152
|
-
async paginate({ page = 1, limit =
|
|
155
|
+
async paginate({ page = 1, limit = 10, orderBy = this._defaultOrder, order = "asc", search = '', filters = [] }) {
|
|
153
156
|
try {
|
|
154
157
|
const pagination = await this._repository.paginate({ page, limit, orderBy, order, search, filters });
|
|
155
158
|
if (this.transformRead) {
|
|
@@ -162,9 +165,9 @@ class AbstractService {
|
|
|
162
165
|
throw e;
|
|
163
166
|
}
|
|
164
167
|
}
|
|
165
|
-
async find({ orderBy = '', order = false, search = '', filters = [] }) {
|
|
168
|
+
async find({ orderBy = '', order = false, search = '', filters = [], limit = 0, }) {
|
|
166
169
|
try {
|
|
167
|
-
let items = await this._repository.find({ orderBy, order, search, filters });
|
|
170
|
+
let items = await this._repository.find({ orderBy, order, search, filters, limit });
|
|
168
171
|
return items;
|
|
169
172
|
}
|
|
170
173
|
catch (e) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
const PaginateBodyRequestSchema = z.object({
|
|
3
|
+
page: z.number(),
|
|
4
|
+
limit: z.number(),
|
|
5
|
+
orderBy: z.string().optional(),
|
|
6
|
+
order: z.enum(["asc", "desc"]).optional(),
|
|
7
|
+
search: z.string().optional(),
|
|
8
|
+
filters: z.array(z.object({
|
|
9
|
+
field: z.string(),
|
|
10
|
+
operator: z.string(),
|
|
11
|
+
value: z.any(),
|
|
12
|
+
}))
|
|
13
|
+
});
|
|
14
|
+
const PaginateBodyResponseSchema = z.object({
|
|
15
|
+
page: z.number(),
|
|
16
|
+
limit: z.number(),
|
|
17
|
+
total: z.number(),
|
|
18
|
+
items: z.array(z.any())
|
|
19
|
+
});
|
|
20
|
+
export { PaginateBodyRequestSchema, PaginateBodyResponseSchema };
|