@drax/crud-back 0.4.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/dist/controllers/AbstractFastifyController.js +193 -0
- package/dist/index.js +8 -0
- package/dist/interfaces/ICrudRepository.js +1 -0
- package/dist/interfaces/IEntityPermission.js +1 -0
- package/dist/interfaces/IEntityRepository.js +1 -0
- package/dist/repository/AbstractMongoRepository.js +87 -0
- package/dist/repository/AbstractSqliteRepository.js +121 -0
- package/dist/services/AbstractService.js +122 -0
- package/package.json +45 -0
- package/src/controllers/AbstractFastifyController.ts +209 -0
- package/src/index.ts +19 -0
- package/src/interfaces/ICrudRepository.ts +16 -0
- package/src/interfaces/IEntityPermission.ts +10 -0
- package/src/repository/AbstractMongoRepository.ts +118 -0
- package/src/repository/AbstractSqliteRepository.ts +170 -0
- package/src/services/AbstractService.ts +145 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/types/controllers/AbstractFastifyController.d.ts +37 -0
- package/types/controllers/AbstractFastifyController.d.ts.map +1 -0
- package/types/index.d.ts +8 -0
- package/types/index.d.ts.map +1 -0
- package/types/interfaces/ICrudRepository.d.ts +15 -0
- package/types/interfaces/ICrudRepository.d.ts.map +1 -0
- package/types/interfaces/IEntityPermission.d.ts +10 -0
- package/types/interfaces/IEntityPermission.d.ts.map +1 -0
- package/types/interfaces/IEntityRepository.d.ts +5 -0
- package/types/interfaces/IEntityRepository.d.ts.map +1 -0
- package/types/repository/AbstractMongoRepository.d.ts +23 -0
- package/types/repository/AbstractMongoRepository.d.ts.map +1 -0
- package/types/repository/AbstractSqliteRepository.d.ts +24 -0
- package/types/repository/AbstractSqliteRepository.d.ts.map +1 -0
- package/types/services/AbstractService.d.ts +21 -0
- package/types/services/AbstractService.d.ts.map +1 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import AbstractService from "../services/AbstractService";
|
|
2
|
+
import {ValidationError} from "@drax/common-back";
|
|
3
|
+
import {UnauthorizedError, Rbac} from "@drax/identity-back";
|
|
4
|
+
import type {FastifyReply, FastifyRequest} from "fastify";
|
|
5
|
+
import {IEntityPermission} from "../interfaces/IEntityPermission";
|
|
6
|
+
|
|
7
|
+
declare module 'fastify' {
|
|
8
|
+
interface FastifyRequest {
|
|
9
|
+
rbac?: Rbac;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type CustomRequest = FastifyRequest<{
|
|
14
|
+
Params: {
|
|
15
|
+
id?: string
|
|
16
|
+
ids?: string
|
|
17
|
+
};
|
|
18
|
+
Querystring:{
|
|
19
|
+
page?: number
|
|
20
|
+
limit?: number
|
|
21
|
+
orderBy?: string
|
|
22
|
+
order?: 'asc' | 'desc' | boolean
|
|
23
|
+
search?: string
|
|
24
|
+
}
|
|
25
|
+
}>
|
|
26
|
+
|
|
27
|
+
class AbstractFastifyController<T,C,U>{
|
|
28
|
+
|
|
29
|
+
protected service: AbstractService<T,C,U>
|
|
30
|
+
protected permission: IEntityPermission
|
|
31
|
+
|
|
32
|
+
constructor(service: AbstractService<T, C, U>, permission: IEntityPermission) {
|
|
33
|
+
this.service = service
|
|
34
|
+
this.permission = permission
|
|
35
|
+
console.log("AbstractFastifyController created. Permissions", this.permission)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async findById(request:CustomRequest, reply: FastifyReply) {
|
|
40
|
+
try {
|
|
41
|
+
request.rbac.assertPermission(this.permission.View)
|
|
42
|
+
if(!request.params.id){
|
|
43
|
+
reply.statusCode = 400
|
|
44
|
+
reply.send({error: 'BAD REQUEST'})
|
|
45
|
+
}
|
|
46
|
+
const id = request.params.id
|
|
47
|
+
let item = await this.service.findById(id)
|
|
48
|
+
return item
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.error(e)
|
|
51
|
+
if (e instanceof ValidationError) {
|
|
52
|
+
reply.statusCode = e.statusCode
|
|
53
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
54
|
+
} else if (e instanceof UnauthorizedError) {
|
|
55
|
+
reply.statusCode = e.statusCode
|
|
56
|
+
reply.send({error: e.message})
|
|
57
|
+
} else {
|
|
58
|
+
reply.statusCode = 500
|
|
59
|
+
reply.send({error: 'INTERNAL_SERVER_ERROR'})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async findByIds(request:CustomRequest, reply: FastifyReply) {
|
|
65
|
+
try {
|
|
66
|
+
request.rbac.assertPermission(this.permission.View)
|
|
67
|
+
if(!request.params.ids){
|
|
68
|
+
reply.statusCode = 400
|
|
69
|
+
reply.send({error: 'BAD REQUEST'})
|
|
70
|
+
}
|
|
71
|
+
const ids = request.params.ids.split(",")
|
|
72
|
+
let items = await this.service.findByIds(ids)
|
|
73
|
+
return items
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.error(e)
|
|
76
|
+
if (e instanceof ValidationError) {
|
|
77
|
+
reply.statusCode = e.statusCode
|
|
78
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
79
|
+
} else if (e instanceof UnauthorizedError) {
|
|
80
|
+
reply.statusCode = e.statusCode
|
|
81
|
+
reply.send({error: e.message})
|
|
82
|
+
} else {
|
|
83
|
+
reply.statusCode = 500
|
|
84
|
+
reply.send({error: 'INTERNAL_SERVER_ERROR'})
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async search(request:CustomRequest, reply: FastifyReply) {
|
|
90
|
+
try {
|
|
91
|
+
request.rbac.assertPermission(this.permission.View)
|
|
92
|
+
const search = request.query.search
|
|
93
|
+
let item = await this.service.search(search)
|
|
94
|
+
return item
|
|
95
|
+
} catch (e) {
|
|
96
|
+
console.error(e)
|
|
97
|
+
if (e instanceof ValidationError) {
|
|
98
|
+
reply.statusCode = e.statusCode
|
|
99
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
100
|
+
} else if (e instanceof UnauthorizedError) {
|
|
101
|
+
reply.statusCode = e.statusCode
|
|
102
|
+
reply.send({error: e.message})
|
|
103
|
+
} else {
|
|
104
|
+
reply.statusCode = 500
|
|
105
|
+
reply.send({error: 'INTERNAL_SERVER_ERROR'})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async paginate(request: CustomRequest, reply: FastifyReply) {
|
|
111
|
+
try {
|
|
112
|
+
request.rbac.assertPermission(this.permission.View)
|
|
113
|
+
const page = request.query.page
|
|
114
|
+
const limit = request.query.limit
|
|
115
|
+
const orderBy = request.query.orderBy
|
|
116
|
+
const order = request.query.order
|
|
117
|
+
const search = request.query.search
|
|
118
|
+
let paginateResult = await this.service.paginate({page, limit, orderBy, order, search})
|
|
119
|
+
return paginateResult
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.error(e)
|
|
122
|
+
if (e instanceof ValidationError) {
|
|
123
|
+
reply.statusCode = e.statusCode
|
|
124
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
125
|
+
} else if (e instanceof UnauthorizedError) {
|
|
126
|
+
reply.statusCode = e.statusCode
|
|
127
|
+
reply.send({error: e.message})
|
|
128
|
+
} else {
|
|
129
|
+
reply.statusCode = 500
|
|
130
|
+
reply.send({error: 'INTERNAL_SERVER_ERROR'})
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async create(request: CustomRequest, reply: FastifyReply) {
|
|
136
|
+
try {
|
|
137
|
+
request.rbac.assertPermission(this.permission.Create)
|
|
138
|
+
const payload = request.body
|
|
139
|
+
let item = await this.service.create(payload as C)
|
|
140
|
+
return item
|
|
141
|
+
} catch (e) {
|
|
142
|
+
console.error(e)
|
|
143
|
+
if (e instanceof ValidationError) {
|
|
144
|
+
reply.statusCode = e.statusCode
|
|
145
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
146
|
+
} else if (e instanceof UnauthorizedError) {
|
|
147
|
+
reply.statusCode = e.statusCode
|
|
148
|
+
reply.send({error: e.message})
|
|
149
|
+
} else {
|
|
150
|
+
reply.statusCode = 500
|
|
151
|
+
reply.send({error: 'INTERNAL_SERVER_ERROR'})
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async update(request: CustomRequest, reply: FastifyReply) {
|
|
157
|
+
try {
|
|
158
|
+
request.rbac.assertPermission(this.permission.Update)
|
|
159
|
+
if(!request.params.id){
|
|
160
|
+
reply.statusCode = 400
|
|
161
|
+
reply.send({error: 'BAD REQUEST'})
|
|
162
|
+
}
|
|
163
|
+
const id = request.params.id
|
|
164
|
+
const payload = request.body
|
|
165
|
+
let item = await this.service.update(id, payload as U)
|
|
166
|
+
return item
|
|
167
|
+
} catch (e) {
|
|
168
|
+
console.error(e)
|
|
169
|
+
if (e instanceof ValidationError) {
|
|
170
|
+
reply.statusCode = e.statusCode
|
|
171
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
172
|
+
} else if (e instanceof UnauthorizedError) {
|
|
173
|
+
reply.statusCode = e.statusCode
|
|
174
|
+
reply.send({error: e.message})
|
|
175
|
+
} else {
|
|
176
|
+
reply.statusCode = 500
|
|
177
|
+
reply.send({error: 'INTERNAL_SERVER_ERROR'})
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async delete(request: CustomRequest, reply: FastifyReply) {
|
|
183
|
+
try {
|
|
184
|
+
request.rbac.assertPermission(this.permission.Delete)
|
|
185
|
+
if(!request.params.id){
|
|
186
|
+
reply.statusCode = 400
|
|
187
|
+
reply.send({error: 'BAD REQUEST'})
|
|
188
|
+
}
|
|
189
|
+
const id = request.params.id
|
|
190
|
+
await this.service.delete(id)
|
|
191
|
+
reply.send({message: 'Item deleted successfully'})
|
|
192
|
+
} catch (e) {
|
|
193
|
+
console.error(e)
|
|
194
|
+
if (e instanceof ValidationError) {
|
|
195
|
+
reply.statusCode = e.statusCode
|
|
196
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
197
|
+
} else if (e instanceof UnauthorizedError) {
|
|
198
|
+
reply.statusCode = e.statusCode
|
|
199
|
+
reply.send({error: e.message})
|
|
200
|
+
} else {
|
|
201
|
+
reply.statusCode = 500
|
|
202
|
+
reply.send({error: 'INTERNAL_SERVER_ERROR'})
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export default AbstractFastifyController;
|
|
209
|
+
export {AbstractFastifyController}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//CRUD
|
|
2
|
+
import AbstractMongoRepository from "./repository/AbstractMongoRepository.js";
|
|
3
|
+
import AbstractSqliteRepository from "./repository/AbstractSqliteRepository.js";
|
|
4
|
+
import AbstractService from "./services/AbstractService.js";
|
|
5
|
+
import AbstractFastifyController from "./controllers/AbstractFastifyController.js";
|
|
6
|
+
|
|
7
|
+
import type {ICrudRepository} from "./interfaces/ICrudRepository";
|
|
8
|
+
|
|
9
|
+
export type {ICrudRepository}
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
|
|
13
|
+
//CRUD
|
|
14
|
+
AbstractMongoRepository,
|
|
15
|
+
AbstractSqliteRepository,
|
|
16
|
+
AbstractService,
|
|
17
|
+
AbstractFastifyController,
|
|
18
|
+
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type {IDraxPaginateOptions, IDraxPaginateResult} from "@drax/common-share";
|
|
2
|
+
|
|
3
|
+
interface ICrudRepository<T,C,U>{
|
|
4
|
+
paginate(options: IDraxPaginateOptions): Promise<IDraxPaginateResult<T>>
|
|
5
|
+
create(input: C): Promise<T>
|
|
6
|
+
update(id: string, input: U): Promise<T>
|
|
7
|
+
delete(id: string): Promise<any>
|
|
8
|
+
|
|
9
|
+
findById?(id: string): Promise<T | null>
|
|
10
|
+
findByIds?(ids: Array<string>): Promise<T[]>
|
|
11
|
+
findOneBy?(field: string, value: any): Promise<T | null>
|
|
12
|
+
findBy?(field: string, value: any): Promise<T[]>
|
|
13
|
+
fetchAll?(): Promise<T[]>
|
|
14
|
+
search?(value: any, limit ?: number): Promise<T[]>
|
|
15
|
+
}
|
|
16
|
+
export type { ICrudRepository }
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import "mongoose-paginate-v2";
|
|
2
|
+
import mongoose from "mongoose";
|
|
3
|
+
import {MongooseQueryFilter, MongooseSort, MongooseErrorToValidationError} from "@drax/common-back";
|
|
4
|
+
import type {DeleteResult} from "mongodb";
|
|
5
|
+
import type {IDraxPaginateOptions, IDraxPaginateResult} from "@drax/common-share";
|
|
6
|
+
import type {PaginateModel, PaginateOptions, PaginateResult} from "mongoose";
|
|
7
|
+
import type {ICrudRepository} from "../interfaces/ICrudRepository";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AbstractMongoRepository<T,C,U> implements ICrudRepository<T,C,U> {
|
|
12
|
+
|
|
13
|
+
_model: mongoose.Model<T> & PaginateModel<T>
|
|
14
|
+
_searchFields: string[] = []
|
|
15
|
+
_populateFields: string[] = []
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async create(data: C): Promise<T> {
|
|
19
|
+
try {
|
|
20
|
+
const item: mongoose.HydratedDocument<T> = new this._model(data)
|
|
21
|
+
await item.save()
|
|
22
|
+
|
|
23
|
+
//@ts-ignore
|
|
24
|
+
await item.populate(this._populateFields)
|
|
25
|
+
|
|
26
|
+
return item
|
|
27
|
+
} catch (e) {
|
|
28
|
+
if (e instanceof mongoose.Error.ValidationError) {
|
|
29
|
+
throw MongooseErrorToValidationError(e)
|
|
30
|
+
}
|
|
31
|
+
throw e
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async update(id: string, data: U): Promise<T> {
|
|
36
|
+
try {
|
|
37
|
+
const item: mongoose.HydratedDocument<T> = await this._model.findOneAndUpdate({_id: id}, data, {new: true}).populate(this._populateFields).exec()
|
|
38
|
+
return item
|
|
39
|
+
} catch (e) {
|
|
40
|
+
if (e instanceof mongoose.Error.ValidationError) {
|
|
41
|
+
throw MongooseErrorToValidationError(e)
|
|
42
|
+
}
|
|
43
|
+
throw e
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async delete(id: string): Promise<boolean> {
|
|
48
|
+
const result: DeleteResult = await this._model.deleteOne({_id: id}).exec()
|
|
49
|
+
return result.deletedCount == 1
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async findById(id: string): Promise<T | null> {
|
|
53
|
+
const item: mongoose.HydratedDocument<T> | null = await this._model.findById(id).populate(this._populateFields).exec()
|
|
54
|
+
return item
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async findByIds(ids: Array<string>): Promise<T[]> {
|
|
58
|
+
const items: mongoose.HydratedDocument<T>[] = await this._model.find({_id: {$in: ids}}).populate(this._populateFields).exec()
|
|
59
|
+
return items
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async findOneBy(field: string, value: any): Promise<T | null> {
|
|
63
|
+
const filter: any = {[field]: value}
|
|
64
|
+
const item: mongoose.HydratedDocument<T> | null = await this._model.findOne(filter).populate(this._populateFields).exec()
|
|
65
|
+
return item
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async findBy(field: string, value: any): Promise<T[]> {
|
|
69
|
+
const filter: any = {[field]: value}
|
|
70
|
+
const items: mongoose.HydratedDocument<T>[] = await this._model.find(filter).populate(this._populateFields).exec()
|
|
71
|
+
return items
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async fetchAll(): Promise<T[]> {
|
|
75
|
+
const items: mongoose.HydratedDocument<T>[] = await this._model.find().populate(this._populateFields).exec()
|
|
76
|
+
return items
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async search(value: string, limit : number = 1000): Promise<T[]> {
|
|
80
|
+
const query = {}
|
|
81
|
+
query['$or'] = this._searchFields.map(field => ({[field]: new RegExp(value, 'i')}))
|
|
82
|
+
const items: mongoose.HydratedDocument<T>[] = await this._model.find(query).limit(limit).exec()
|
|
83
|
+
return items
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async paginate({
|
|
88
|
+
page = 1,
|
|
89
|
+
limit = 5,
|
|
90
|
+
orderBy = '',
|
|
91
|
+
order = false,
|
|
92
|
+
search = '',
|
|
93
|
+
filters = []
|
|
94
|
+
}: IDraxPaginateOptions): Promise<IDraxPaginateResult<T>> {
|
|
95
|
+
|
|
96
|
+
const query = {}
|
|
97
|
+
|
|
98
|
+
if (search) {
|
|
99
|
+
query['$or'] = this._searchFields.map(field => ({[field]: new RegExp(search, 'i')}))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
MongooseQueryFilter.applyFilters(query, filters)
|
|
103
|
+
|
|
104
|
+
const sort = MongooseSort.applySort(orderBy, order)
|
|
105
|
+
const populate = this._populateFields
|
|
106
|
+
const options = {page, limit, sort, populate} as PaginateOptions
|
|
107
|
+
const items: PaginateResult<T> = await this._model.paginate(query, options)
|
|
108
|
+
return {
|
|
109
|
+
page: page,
|
|
110
|
+
limit: limit,
|
|
111
|
+
total: items.totalDocs,
|
|
112
|
+
items: items.docs
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export default AbstractMongoRepository
|
|
118
|
+
export {AbstractMongoRepository}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import sqlite from "better-sqlite3";
|
|
2
|
+
import type {IDraxPaginateOptions, IDraxPaginateResult} from "@drax/common-share";
|
|
3
|
+
import {randomUUID} from "node:crypto";
|
|
4
|
+
import {
|
|
5
|
+
SqlSort, SqlQueryFilter, SqliteTableBuilder, SqliteTableField,
|
|
6
|
+
SqliteErrorToValidationError} from "@drax/common-back";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AbstractSqliteRepository<T>{
|
|
11
|
+
protected db: any;
|
|
12
|
+
protected tableName: string;
|
|
13
|
+
protected dataBase: string;
|
|
14
|
+
protected searchFields: string[] = [];
|
|
15
|
+
protected booleanFields: string[] = [];
|
|
16
|
+
protected identifier: string = 'id';
|
|
17
|
+
protected verbose: boolean;
|
|
18
|
+
protected tableFields: SqliteTableField[];
|
|
19
|
+
|
|
20
|
+
constructor(dataBase:string, tableName: string, identifier:string = 'id',searchFields:string[] = [], booleanFields:string[] = [], verbose:boolean = false) {
|
|
21
|
+
if(!dataBase){
|
|
22
|
+
throw new Error("dataBase is required")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if(!tableName){
|
|
26
|
+
throw new Error("tableName is required")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
this.dataBase = dataBase;
|
|
31
|
+
this.tableName = tableName
|
|
32
|
+
this.identifier = identifier
|
|
33
|
+
this.searchFields = searchFields
|
|
34
|
+
this.booleanFields = booleanFields
|
|
35
|
+
this.verbose = verbose;
|
|
36
|
+
this.db = new sqlite(dataBase, {verbose: verbose ? console.log : null});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
build() {
|
|
40
|
+
const builder = new SqliteTableBuilder(this.dataBase, this.tableName, this.tableFields, this.verbose);
|
|
41
|
+
builder.build(this.identifier)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async create(data: any): Promise<T> {
|
|
45
|
+
try{
|
|
46
|
+
|
|
47
|
+
if(!data[this.identifier]){
|
|
48
|
+
data[this.identifier] = randomUUID()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for(const key in data){
|
|
52
|
+
if(typeof data[key] === 'boolean'){
|
|
53
|
+
data[key] = data[key]? 1 : 0
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const fields = Object.keys(data)
|
|
58
|
+
.map(field => `${field}`)
|
|
59
|
+
.join(', ');
|
|
60
|
+
|
|
61
|
+
const values = Object.keys(data)
|
|
62
|
+
.map(field => `@${field}`)
|
|
63
|
+
.join(', ');
|
|
64
|
+
|
|
65
|
+
const stmt = this.db.prepare(`INSERT INTO ${this.tableName} (${fields}) VALUES (${values})`);
|
|
66
|
+
stmt.run(data)
|
|
67
|
+
return this.findById(data[this.identifier])
|
|
68
|
+
}catch (e){
|
|
69
|
+
console.log(e)
|
|
70
|
+
throw SqliteErrorToValidationError(e, data)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async findById(id: string): Promise<T | null>{
|
|
75
|
+
const item = this.db.prepare(`SELECT * FROM ${this.tableName} WHERE ${this.identifier} = ?`).get(id);
|
|
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
|
|
82
|
+
}
|
|
83
|
+
|
|
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
|
+
|
|
89
|
+
async update(id: string, data: any): Promise<T> {
|
|
90
|
+
try{
|
|
91
|
+
|
|
92
|
+
for(const key in data){
|
|
93
|
+
if(typeof data[key] === 'boolean'){
|
|
94
|
+
data[key] = data[key]? 1 : 0
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const setClauses = Object.keys(data)
|
|
99
|
+
.map(field => `${field} = @${field}`)
|
|
100
|
+
.join(', ');
|
|
101
|
+
data.identifier = id
|
|
102
|
+
const stmt = this.db.prepare( `UPDATE ${this.tableName} SET ${setClauses} WHERE ${this.identifier} = @identifier `);
|
|
103
|
+
stmt.run(data);
|
|
104
|
+
return this.findById(id)
|
|
105
|
+
}catch (e){
|
|
106
|
+
console.log(e)
|
|
107
|
+
throw SqliteErrorToValidationError(e, data)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
async delete(id: string): Promise<boolean> {
|
|
114
|
+
const stmt = this.db.prepare(`DELETE FROM ${this.tableName} WHERE ${this.identifier} = ?`);
|
|
115
|
+
stmt.run(id);
|
|
116
|
+
return true
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async paginate({
|
|
122
|
+
page= 1,
|
|
123
|
+
limit= 5,
|
|
124
|
+
orderBy= '',
|
|
125
|
+
order= 'desc',
|
|
126
|
+
search= '',
|
|
127
|
+
filters= []} : IDraxPaginateOptions): Promise<IDraxPaginateResult<T>>{
|
|
128
|
+
|
|
129
|
+
const offset = page > 1 ? (page - 1) * limit : 0
|
|
130
|
+
|
|
131
|
+
let where=""
|
|
132
|
+
if (search && this.searchFields.length > 0) {
|
|
133
|
+
where = ` WHERE ${this.searchFields.map(field => `${field} LIKE '%${search}%'`).join(" OR ")}`
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (filters.length > 0) {
|
|
137
|
+
where = SqlQueryFilter.applyFilters(where, filters)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const sort = SqlSort.applySort(orderBy, order)
|
|
141
|
+
|
|
142
|
+
const rCount = this.db.prepare(`SELECT COUNT(*) as count FROM ${this.tableName} ${where}`).get();
|
|
143
|
+
const items = this.db.prepare(`SELECT * FROM ${this.tableName} ${where} ${sort} LIMIT ? OFFSET ? `).all([limit, offset]) as T[];
|
|
144
|
+
|
|
145
|
+
for(const item of items){
|
|
146
|
+
for(const key in item) {
|
|
147
|
+
if (this.booleanFields.includes(key)) {
|
|
148
|
+
//@ts-ignore
|
|
149
|
+
item[key] = item[key] ? true : false
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
page: page,
|
|
156
|
+
limit: limit,
|
|
157
|
+
total: rCount.count,
|
|
158
|
+
items: items
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async fetchAll(): Promise<any[]>{
|
|
163
|
+
const tenants = this.db.prepare(`SELECT * FROM ${this.tableName}`).all();
|
|
164
|
+
|
|
165
|
+
return tenants
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export default AbstractSqliteRepository
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {ValidationError, ZodErrorToValidationError} from "@drax/common-back"
|
|
2
|
+
import {ZodError} from "zod";
|
|
3
|
+
import type {ZodSchema} from "zod";
|
|
4
|
+
import type {IDraxPaginateOptions, IDraxPaginateResult} from "@drax/common-share";
|
|
5
|
+
import type {ICrudRepository} from "../interfaces/ICrudRepository";
|
|
6
|
+
|
|
7
|
+
class AbstractService<T,C,U> {
|
|
8
|
+
|
|
9
|
+
_repository: ICrudRepository<T,C,U>
|
|
10
|
+
_schema?: ZodSchema | undefined
|
|
11
|
+
|
|
12
|
+
constructor(repository: ICrudRepository<T,C,U>, schema?: ZodSchema) {
|
|
13
|
+
this._repository = repository
|
|
14
|
+
this._schema = schema
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async create(data: C): Promise<T> {
|
|
18
|
+
try {
|
|
19
|
+
if(this._schema){
|
|
20
|
+
await this._schema.parseAsync(data)
|
|
21
|
+
}
|
|
22
|
+
const item: T = await this._repository.create(data)
|
|
23
|
+
return item
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.error("Error creating", e)
|
|
26
|
+
if (e instanceof ZodError) {
|
|
27
|
+
throw ZodErrorToValidationError(e, data)
|
|
28
|
+
}
|
|
29
|
+
throw e
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async update(id: string, data: U): Promise<T> {
|
|
34
|
+
try {
|
|
35
|
+
if(this._schema){
|
|
36
|
+
await this._schema.parseAsync(data)
|
|
37
|
+
}
|
|
38
|
+
const item : T = await this._repository.update(id, data)
|
|
39
|
+
return item
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.error("Error updating", e)
|
|
42
|
+
if (e instanceof ZodError) {
|
|
43
|
+
throw ZodErrorToValidationError(e, data)
|
|
44
|
+
}
|
|
45
|
+
throw e
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async delete(id: string): Promise<boolean> {
|
|
50
|
+
try {
|
|
51
|
+
const deleted = await this._repository.delete(id);
|
|
52
|
+
return deleted;
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.error("Error deleting", e)
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async findById(id: string): Promise<T | null> {
|
|
61
|
+
try {
|
|
62
|
+
const item: T = await this._repository.findById(id);
|
|
63
|
+
return item
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error("Error finding Auto by id", e)
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async findByIds(ids: Array<string>): Promise<T[]> {
|
|
71
|
+
try {
|
|
72
|
+
const items: T[] = await this._repository.findByIds(ids);
|
|
73
|
+
return items
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.error("Error finding Auto by id", e)
|
|
76
|
+
throw e;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async findOneBy(field: string, value: string): Promise<T | null> {
|
|
81
|
+
try {
|
|
82
|
+
const item: T = await this._repository.findOneBy(field, value);
|
|
83
|
+
return item
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.error("Error finding Auto findOneBy", e)
|
|
86
|
+
throw e;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async findBy(field: string, value: string): Promise<T[] | null> {
|
|
92
|
+
try {
|
|
93
|
+
const items: T[] = await this._repository.findBy(field, value);
|
|
94
|
+
return items
|
|
95
|
+
} catch (e) {
|
|
96
|
+
console.error("Error finding Auto findBy", e)
|
|
97
|
+
throw e;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async fetchAll(): Promise<T[]> {
|
|
103
|
+
try {
|
|
104
|
+
const items: T[] = await this._repository.fetchAll();
|
|
105
|
+
return items
|
|
106
|
+
} catch (e) {
|
|
107
|
+
console.error("Error fetching all Autos", e)
|
|
108
|
+
throw e;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async search(value: string): Promise<T[]> {
|
|
114
|
+
try {
|
|
115
|
+
const items: T[] = await this._repository.search(value);
|
|
116
|
+
return items
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.error("Error fetching all Autos", e)
|
|
119
|
+
throw e;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async paginate({
|
|
125
|
+
page = 1,
|
|
126
|
+
limit = 5,
|
|
127
|
+
orderBy = '',
|
|
128
|
+
order = false,
|
|
129
|
+
search = '',
|
|
130
|
+
filters = []
|
|
131
|
+
}: IDraxPaginateOptions): Promise<IDraxPaginateResult<T>> {
|
|
132
|
+
try {
|
|
133
|
+
const pagination = await this._repository.paginate({page, limit, orderBy, order, search, filters});
|
|
134
|
+
return pagination;
|
|
135
|
+
} catch (e) {
|
|
136
|
+
console.error("Error paginating", e)
|
|
137
|
+
throw e;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export default AbstractService
|
|
145
|
+
export {AbstractService}
|