@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.
Files changed (74) hide show
  1. package/dist/builders/CrudSchemaBuilder.js +349 -0
  2. package/dist/controllers/AbstractFastifyController.js +105 -176
  3. package/dist/index.js +15 -1
  4. package/dist/regexs/QueryFilterRegex.js +3 -0
  5. package/dist/repository/AbstractMongoRepository.js +114 -28
  6. package/dist/repository/AbstractSqliteRepository.js +131 -41
  7. package/dist/schemas/DeleteBodyResponseSchema.js +9 -0
  8. package/dist/schemas/ErrorBodyResponseSchema.js +16 -0
  9. package/dist/schemas/ExportBodyResponseSchema.js +9 -0
  10. package/dist/schemas/FindBySchema.js +6 -0
  11. package/dist/schemas/FindSchema.js +9 -0
  12. package/dist/schemas/IdParamSchema.js +6 -0
  13. package/dist/schemas/PaginateBodySchema.js +20 -0
  14. package/dist/schemas/PaginateSchema.js +17 -0
  15. package/dist/schemas/SearchSchema.js +5 -0
  16. package/dist/services/AbstractService.js +8 -5
  17. package/dist/zod/DeleteBodySchema.js +6 -0
  18. package/dist/zod/IdParamSchema.js +6 -0
  19. package/dist/zod/PaginateBodySchema.js +20 -0
  20. package/package.json +7 -6
  21. package/src/builders/CrudSchemaBuilder.ts +420 -0
  22. package/src/controllers/AbstractFastifyController.ts +149 -165
  23. package/src/index.ts +28 -0
  24. package/src/regexs/QueryFilterRegex.ts +4 -0
  25. package/src/repository/AbstractMongoRepository.ts +153 -40
  26. package/src/repository/AbstractSqliteRepository.ts +196 -72
  27. package/src/schemas/DeleteBodyResponseSchema.ts +12 -0
  28. package/src/schemas/ErrorBodyResponseSchema.ts +20 -0
  29. package/src/schemas/ExportBodyResponseSchema.ts +12 -0
  30. package/src/schemas/FindBySchema.ts +9 -0
  31. package/src/schemas/FindSchema.ts +13 -0
  32. package/src/schemas/IdParamSchema.ts +9 -0
  33. package/src/schemas/PaginateSchema.ts +21 -0
  34. package/src/schemas/SearchSchema.ts +8 -0
  35. package/src/services/AbstractService.ts +42 -33
  36. package/tsconfig.tsbuildinfo +1 -1
  37. package/types/builders/CrudSchemaBuilder.d.ts +2401 -0
  38. package/types/builders/CrudSchemaBuilder.d.ts.map +1 -0
  39. package/types/controllers/AbstractFastifyController.d.ts +6 -1
  40. package/types/controllers/AbstractFastifyController.d.ts.map +1 -1
  41. package/types/index.d.ts +10 -1
  42. package/types/index.d.ts.map +1 -1
  43. package/types/regexs/QueryFilterRegex.d.ts +4 -0
  44. package/types/regexs/QueryFilterRegex.d.ts.map +1 -0
  45. package/types/repository/AbstractMongoRepository.d.ts +6 -3
  46. package/types/repository/AbstractMongoRepository.d.ts.map +1 -1
  47. package/types/repository/AbstractSqliteRepository.d.ts +23 -8
  48. package/types/repository/AbstractSqliteRepository.d.ts.map +1 -1
  49. package/types/schemas/DeleteBodyResponseSchema.d.ts +20 -0
  50. package/types/schemas/DeleteBodyResponseSchema.d.ts.map +1 -0
  51. package/types/schemas/ErrorBodyResponseSchema.d.ts +60 -0
  52. package/types/schemas/ErrorBodyResponseSchema.d.ts.map +1 -0
  53. package/types/schemas/ExportBodyResponseSchema.d.ts +20 -0
  54. package/types/schemas/ExportBodyResponseSchema.d.ts.map +1 -0
  55. package/types/schemas/FindBySchema.d.ts +13 -0
  56. package/types/schemas/FindBySchema.d.ts.map +1 -0
  57. package/types/schemas/FindSchema.d.ts +19 -0
  58. package/types/schemas/FindSchema.d.ts.map +1 -0
  59. package/types/schemas/IdParamSchema.d.ts +11 -0
  60. package/types/schemas/IdParamSchema.d.ts.map +1 -0
  61. package/types/schemas/PaginateBodySchema.d.ts +61 -0
  62. package/types/schemas/PaginateBodySchema.d.ts.map +1 -0
  63. package/types/schemas/PaginateSchema.d.ts +41 -0
  64. package/types/schemas/PaginateSchema.d.ts.map +1 -0
  65. package/types/schemas/SearchSchema.d.ts +10 -0
  66. package/types/schemas/SearchSchema.d.ts.map +1 -0
  67. package/types/services/AbstractService.d.ts +5 -5
  68. package/types/services/AbstractService.d.ts.map +1 -1
  69. package/types/zod/DeleteBodySchema.d.ts +11 -0
  70. package/types/zod/DeleteBodySchema.d.ts.map +1 -0
  71. package/types/zod/IdParamSchema.d.ts +11 -0
  72. package/types/zod/IdParamSchema.d.ts.map +1 -0
  73. package/types/zod/PaginateBodySchema.d.ts +61 -0
  74. 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, MongooseErrorToValidationError } from "@drax/common-back";
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 = new this._model(data);
12
- await item.save();
13
- //@ts-ignore
14
- await item.populate(this._populateFields);
15
- return item;
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 MongooseErrorToValidationError(e);
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 MongooseErrorToValidationError(e);
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 MongooseErrorToValidationError(e);
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.findById(id).populate(this._populateFields).exec();
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.find({ _id: { $in: ids } }).populate(this._populateFields).exec();
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.findOne(filter).populate(this._populateFields).exec();
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.find(filter).populate(this._populateFields).exec();
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.find().populate(this._populateFields).exec();
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.find(query).limit(limit).exec();
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 = false, search = '', filters = [] }) {
143
+ async paginate({ page = 1, limit = 5, orderBy = '', order = "asc", search = '', filters = [] }) {
84
144
  const query = {};
85
145
  if (search) {
86
- query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(search, 'i') }));
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 options = { page, limit, sort, populate };
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
- query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(search, 'i') }));
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 populate = this._populateFields;
107
- return this._model.findOne(query).populate(populate);
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
- query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(search, 'i') }));
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 populate = this._populateFields;
117
- return this._model.find(query).limit(limit).sort(sort).populate(populate);
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(dataBase, tableName, identifier = 'id', searchFields = [], booleanFields = [], verbose = false) {
5
+ constructor(dataBaseFile, verbose = false) {
6
+ this.tableName = '';
6
7
  this.searchFields = [];
7
8
  this.booleanFields = [];
8
- this.identifier = 'id';
9
- if (!dataBase) {
10
- throw new Error("dataBase is required");
11
- }
12
- if (!tableName) {
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(dataBase, { verbose: verbose ? console.log : null });
15
+ this.db = new sqlite(dataBaseFile, { verbose: verbose ? console.log : null });
22
16
  }
23
17
  build() {
24
- const builder = new SqliteTableBuilder(this.dataBase, this.tableName, this.tableFields, this.verbose);
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}) VALUES (${values})`);
78
+ const stmt = this.db.prepare(`INSERT INTO ${this.tableName} (${fields})
79
+ VALUES (${values})`);
44
80
  stmt.run(data);
45
- return this.findById(data[this.identifier]);
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 findById(id) {
53
- const item = this.db.prepare(`SELECT * FROM ${this.tableName} WHERE ${this.identifier} = ?`).get(id);
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} SET ${setClauses} WHERE ${this.identifier} = @identifier `);
107
+ const stmt = this.db.prepare(`UPDATE ${this.tableName}
108
+ SET ${setClauses}
109
+ WHERE ${this.identifier} = @identifier `);
76
110
  stmt.run(data);
77
- return this.findById(id);
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 FROM ${this.tableName} WHERE ${this.identifier} = ?`);
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 FROM ${this.tableName} ${where}`).get();
100
- const items = this.db.prepare(`SELECT * FROM ${this.tableName} ${where} ${sort} LIMIT ? OFFSET ? `).all([limit, offset]);
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
- for (const key in item) {
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 * FROM ${this.tableName}`).all();
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 * FROM ${this.tableName} ${where}`).all();
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,9 @@
1
+ import z from "zod";
2
+ const DeleteBodyResponseSchema = z.object({
3
+ id: z.string(),
4
+ deleted: z.boolean(),
5
+ deletedAt: z.string(),
6
+ message: z.string()
7
+ });
8
+ export default DeleteBodyResponseSchema;
9
+ export { DeleteBodyResponseSchema };
@@ -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
+ const ExportBodyResponseSchema = z.object({
3
+ url: z.string(),
4
+ rowCount: z.number(),
5
+ time: z.number(),
6
+ fileName: z.string(),
7
+ });
8
+ export default ExportBodyResponseSchema;
9
+ export { ExportBodyResponseSchema };
@@ -0,0 +1,6 @@
1
+ import z from "zod";
2
+ const FindByParamSchema = z.object({
3
+ value: z.string(),
4
+ field: z.string(),
5
+ });
6
+ export { FindByParamSchema };
@@ -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,6 @@
1
+ import z from "zod";
2
+ const IdParamSchema = z.object({
3
+ id: z.string().min(1, "validation.required")
4
+ });
5
+ export default IdParamSchema;
6
+ export { IdParamSchema };
@@ -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 };
@@ -0,0 +1,5 @@
1
+ import z from "zod";
2
+ const SearchQuerySchema = z.object({
3
+ search: z.string().optional(),
4
+ });
5
+ export { SearchQuerySchema };
@@ -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 = 5, orderBy = this._defaultOrder, order = false, search = '', filters = [] }) {
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,6 @@
1
+ import z from "zod";
2
+ const DeleteBodySchema = z.object({
3
+ message: z.string(),
4
+ });
5
+ export default DeleteBodySchema;
6
+ export { DeleteBodySchema };
@@ -0,0 +1,6 @@
1
+ import z from "zod";
2
+ const IdParamSchema = z.object({
3
+ id: z.string().min(1, "validation.required")
4
+ });
5
+ export default IdParamSchema;
6
+ export { IdParamSchema };
@@ -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 };