@drax/crud-back 0.4.0 → 0.5.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/controllers/AbstractFastifyController.js +113 -44
- package/dist/exports/AbstractExport.js +41 -0
- package/dist/exports/ExportCsv.js +86 -0
- package/dist/exports/ExportJson.js +51 -0
- package/dist/repository/AbstractMongoRepository.js +11 -0
- package/dist/services/AbstractService.js +44 -0
- package/dist/workers/ExportCsvWorker.js +12 -0
- package/package.json +8 -6
- package/src/controllers/AbstractFastifyController.ts +130 -46
- package/src/exports/AbstractExport.ts +74 -0
- package/src/exports/ExportCsv.ts +107 -0
- package/src/exports/ExportJson.ts +64 -0
- package/src/index.ts +0 -3
- package/src/repository/AbstractMongoRepository.ts +25 -5
- package/src/repository/AbstractSqliteRepository.ts +1 -1
- package/src/services/AbstractService.ts +81 -10
- package/src/workers/ExportCsvWorker.js +12 -0
- package/src/workers/ExportCsvWorker.ts +12 -0
- package/test/_mocks/MockRepository.ts +58 -0
- package/test/exports/ExportCsv.test.ts +137 -0
- package/test/services/AbstractService.test.ts +40 -0
- package/test/workers/ExportCsvWorker.test.ts +92 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/types/controllers/AbstractFastifyController.d.ts +19 -7
- package/types/controllers/AbstractFastifyController.d.ts.map +1 -1
- package/types/exports/AbstractExport.d.ts +23 -0
- package/types/exports/AbstractExport.d.ts.map +1 -0
- package/types/exports/ExportCsv.d.ts +13 -0
- package/types/exports/ExportCsv.d.ts.map +1 -0
- package/types/exports/ExportFsCsv.d.ts +17 -0
- package/types/exports/ExportFsCsv.d.ts.map +1 -0
- package/types/exports/ExportJson.d.ts +8 -0
- package/types/exports/ExportJson.d.ts.map +1 -0
- package/types/index.d.ts +0 -2
- package/types/index.d.ts.map +1 -1
- package/types/interfaces/ICrudRepository.d.ts +2 -1
- package/types/interfaces/ICrudRepository.d.ts.map +1 -1
- package/types/repository/AbstractMongoRepository.d.ts +3 -3
- package/types/repository/AbstractMongoRepository.d.ts.map +1 -1
- package/types/repository/AbstractSqliteRepository.d.ts +1 -1
- package/types/repository/AbstractSqliteRepository.d.ts.map +1 -1
- package/types/services/AbstractService.d.ts +8 -5
- package/types/services/AbstractService.d.ts.map +1 -1
- package/types/workers/ExportCsvWorker.d.ts +2 -0
- package/types/workers/ExportCsvWorker.d.ts.map +1 -0
- package/src/interfaces/ICrudRepository.ts +0 -16
- package/src/interfaces/IEntityPermission.ts +0 -10
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import AbstractService from "../services/AbstractService";
|
|
2
|
-
import {ValidationError} from "@drax/common-back";
|
|
2
|
+
import {CommonConfig, DraxConfig, ValidationError} from "@drax/common-back";
|
|
3
3
|
import {UnauthorizedError, Rbac} from "@drax/identity-back";
|
|
4
4
|
import type {FastifyReply, FastifyRequest} from "fastify";
|
|
5
|
-
import {
|
|
5
|
+
import {IDraxExportResult, IDraxPermission} from "@drax/crud-share";
|
|
6
|
+
import {join} from "path";
|
|
6
7
|
|
|
7
8
|
declare module 'fastify' {
|
|
8
9
|
interface FastifyRequest {
|
|
@@ -14,37 +15,88 @@ type CustomRequest = FastifyRequest<{
|
|
|
14
15
|
Params: {
|
|
15
16
|
id?: string
|
|
16
17
|
ids?: string
|
|
18
|
+
format?: string
|
|
17
19
|
};
|
|
18
20
|
Querystring:{
|
|
21
|
+
format?: string
|
|
19
22
|
page?: number
|
|
20
23
|
limit?: number
|
|
21
24
|
orderBy?: string
|
|
22
25
|
order?: 'asc' | 'desc' | boolean
|
|
23
26
|
search?: string
|
|
27
|
+
filters?: string
|
|
28
|
+
headers?: string
|
|
29
|
+
separator?: string
|
|
24
30
|
}
|
|
25
31
|
}>
|
|
26
32
|
|
|
33
|
+
const BASE_FILE_DIR = DraxConfig.getOrLoad(CommonConfig.FileDir) || 'files';
|
|
34
|
+
const BASE_URL = DraxConfig.getOrLoad(CommonConfig.BaseUrl) ? DraxConfig.get(CommonConfig.BaseUrl).replace(/\/$/, '') : ''
|
|
35
|
+
|
|
36
|
+
|
|
27
37
|
class AbstractFastifyController<T,C,U>{
|
|
28
38
|
|
|
29
39
|
protected service: AbstractService<T,C,U>
|
|
30
|
-
protected permission:
|
|
40
|
+
protected permission: IDraxPermission
|
|
31
41
|
|
|
32
|
-
constructor(service: AbstractService<T, C, U>, permission:
|
|
42
|
+
constructor(service: AbstractService<T, C, U>, permission: IDraxPermission) {
|
|
33
43
|
this.service = service
|
|
34
44
|
this.permission = permission
|
|
35
45
|
console.log("AbstractFastifyController created. Permissions", this.permission)
|
|
36
46
|
}
|
|
37
47
|
|
|
48
|
+
parseFilters(stringFilters: string): any {
|
|
49
|
+
try {
|
|
50
|
+
if (!stringFilters) {
|
|
51
|
+
return {}
|
|
52
|
+
}
|
|
53
|
+
const filterArray = stringFilters.split(";")
|
|
54
|
+
const filters = []
|
|
55
|
+
filterArray.forEach((filter) => {
|
|
56
|
+
const [field, operator, value] = filter.split(",")
|
|
57
|
+
filters.push({field, operator, value})
|
|
58
|
+
})
|
|
59
|
+
return filters
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.error(e)
|
|
62
|
+
throw e
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
38
66
|
|
|
39
|
-
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async create(request: CustomRequest, reply: FastifyReply) {
|
|
40
70
|
try {
|
|
41
|
-
request.rbac.assertPermission(this.permission.
|
|
71
|
+
request.rbac.assertPermission(this.permission.Create)
|
|
72
|
+
const payload = request.body
|
|
73
|
+
let item = await this.service.create(payload as C)
|
|
74
|
+
return item
|
|
75
|
+
} catch (e) {
|
|
76
|
+
console.error(e)
|
|
77
|
+
if (e instanceof ValidationError) {
|
|
78
|
+
reply.statusCode = e.statusCode
|
|
79
|
+
reply.send({error: e.message, inputErrors: e.errors})
|
|
80
|
+
} else if (e instanceof UnauthorizedError) {
|
|
81
|
+
reply.statusCode = e.statusCode
|
|
82
|
+
reply.send({error: e.message})
|
|
83
|
+
} else {
|
|
84
|
+
reply.statusCode = 500
|
|
85
|
+
reply.send({error: 'INTERNAL_SERVER_ERROR'})
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async update(request: CustomRequest, reply: FastifyReply) {
|
|
91
|
+
try {
|
|
92
|
+
request.rbac.assertPermission(this.permission.Update)
|
|
42
93
|
if(!request.params.id){
|
|
43
94
|
reply.statusCode = 400
|
|
44
95
|
reply.send({error: 'BAD REQUEST'})
|
|
45
96
|
}
|
|
46
97
|
const id = request.params.id
|
|
47
|
-
|
|
98
|
+
const payload = request.body
|
|
99
|
+
let item = await this.service.update(id, payload as U)
|
|
48
100
|
return item
|
|
49
101
|
} catch (e) {
|
|
50
102
|
console.error(e)
|
|
@@ -61,16 +113,16 @@ class AbstractFastifyController<T,C,U>{
|
|
|
61
113
|
}
|
|
62
114
|
}
|
|
63
115
|
|
|
64
|
-
async
|
|
116
|
+
async delete(request: CustomRequest, reply: FastifyReply) {
|
|
65
117
|
try {
|
|
66
|
-
request.rbac.assertPermission(this.permission.
|
|
67
|
-
if(!request.params.
|
|
118
|
+
request.rbac.assertPermission(this.permission.Delete)
|
|
119
|
+
if(!request.params.id){
|
|
68
120
|
reply.statusCode = 400
|
|
69
121
|
reply.send({error: 'BAD REQUEST'})
|
|
70
122
|
}
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
123
|
+
const id = request.params.id
|
|
124
|
+
await this.service.delete(id)
|
|
125
|
+
reply.send({message: 'Item deleted successfully'})
|
|
74
126
|
} catch (e) {
|
|
75
127
|
console.error(e)
|
|
76
128
|
if (e instanceof ValidationError) {
|
|
@@ -86,11 +138,15 @@ class AbstractFastifyController<T,C,U>{
|
|
|
86
138
|
}
|
|
87
139
|
}
|
|
88
140
|
|
|
89
|
-
async
|
|
141
|
+
async findById(request:CustomRequest, reply: FastifyReply) {
|
|
90
142
|
try {
|
|
91
143
|
request.rbac.assertPermission(this.permission.View)
|
|
92
|
-
|
|
93
|
-
|
|
144
|
+
if(!request.params.id){
|
|
145
|
+
reply.statusCode = 400
|
|
146
|
+
reply.send({error: 'BAD REQUEST'})
|
|
147
|
+
}
|
|
148
|
+
const id = request.params.id
|
|
149
|
+
let item = await this.service.findById(id)
|
|
94
150
|
return item
|
|
95
151
|
} catch (e) {
|
|
96
152
|
console.error(e)
|
|
@@ -107,16 +163,16 @@ class AbstractFastifyController<T,C,U>{
|
|
|
107
163
|
}
|
|
108
164
|
}
|
|
109
165
|
|
|
110
|
-
async
|
|
166
|
+
async findByIds(request:CustomRequest, reply: FastifyReply) {
|
|
111
167
|
try {
|
|
112
168
|
request.rbac.assertPermission(this.permission.View)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
let
|
|
119
|
-
return
|
|
169
|
+
if(!request.params.ids){
|
|
170
|
+
reply.statusCode = 400
|
|
171
|
+
reply.send({error: 'BAD REQUEST'})
|
|
172
|
+
}
|
|
173
|
+
const ids = request.params.ids.split(",")
|
|
174
|
+
let items = await this.service.findByIds(ids)
|
|
175
|
+
return items
|
|
120
176
|
} catch (e) {
|
|
121
177
|
console.error(e)
|
|
122
178
|
if (e instanceof ValidationError) {
|
|
@@ -132,11 +188,11 @@ class AbstractFastifyController<T,C,U>{
|
|
|
132
188
|
}
|
|
133
189
|
}
|
|
134
190
|
|
|
135
|
-
async
|
|
191
|
+
async search(request:CustomRequest, reply: FastifyReply) {
|
|
136
192
|
try {
|
|
137
|
-
request.rbac.assertPermission(this.permission.
|
|
138
|
-
const
|
|
139
|
-
let item = await this.service.
|
|
193
|
+
request.rbac.assertPermission(this.permission.View)
|
|
194
|
+
const search = request.query.search
|
|
195
|
+
let item = await this.service.search(search)
|
|
140
196
|
return item
|
|
141
197
|
} catch (e) {
|
|
142
198
|
console.error(e)
|
|
@@ -153,17 +209,17 @@ class AbstractFastifyController<T,C,U>{
|
|
|
153
209
|
}
|
|
154
210
|
}
|
|
155
211
|
|
|
156
|
-
async
|
|
212
|
+
async paginate(request: CustomRequest, reply: FastifyReply) {
|
|
157
213
|
try {
|
|
158
|
-
request.rbac.assertPermission(this.permission.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
let
|
|
166
|
-
return
|
|
214
|
+
request.rbac.assertPermission(this.permission.View)
|
|
215
|
+
const page = request.query.page
|
|
216
|
+
const limit = request.query.limit
|
|
217
|
+
const orderBy = request.query.orderBy
|
|
218
|
+
const order = request.query.order
|
|
219
|
+
const search = request.query.search
|
|
220
|
+
//const filters = this.parseFilters(request.query.filters)
|
|
221
|
+
let paginateResult = await this.service.paginate({page, limit, orderBy, order, search})
|
|
222
|
+
return paginateResult
|
|
167
223
|
} catch (e) {
|
|
168
224
|
console.error(e)
|
|
169
225
|
if (e instanceof ValidationError) {
|
|
@@ -179,16 +235,44 @@ class AbstractFastifyController<T,C,U>{
|
|
|
179
235
|
}
|
|
180
236
|
}
|
|
181
237
|
|
|
182
|
-
async
|
|
238
|
+
async export(request:CustomRequest, reply: FastifyReply) {
|
|
183
239
|
try {
|
|
184
|
-
request.rbac.assertPermission(this.permission.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
240
|
+
request.rbac.assertPermission(this.permission.View)
|
|
241
|
+
|
|
242
|
+
const format = request.query.format as 'CSV'|'JSON' || 'JSON'
|
|
243
|
+
const headers = request.query.headers ? request.query.headers.split(",") : []
|
|
244
|
+
const separator = request.query.separator || ";"
|
|
245
|
+
const limit = request.query.limit
|
|
246
|
+
const orderBy = request.query.orderBy
|
|
247
|
+
const order = request.query.order
|
|
248
|
+
const search = request.query.search
|
|
249
|
+
|
|
250
|
+
const year = (new Date().getFullYear()).toString()
|
|
251
|
+
const month = (new Date().getMonth() + 1).toString().padStart(2, '0')
|
|
252
|
+
const exportPath = 'exports'
|
|
253
|
+
|
|
254
|
+
const destinationPath = join(BASE_FILE_DIR, 'exports',year,month)
|
|
255
|
+
|
|
256
|
+
let result: IDraxExportResult = await this.service.export({
|
|
257
|
+
separator,
|
|
258
|
+
format,
|
|
259
|
+
headers,
|
|
260
|
+
limit,
|
|
261
|
+
orderBy,
|
|
262
|
+
order,
|
|
263
|
+
search,
|
|
264
|
+
}, destinationPath)
|
|
265
|
+
|
|
266
|
+
const url = `${BASE_URL}/api/file/${exportPath}/${year}/${month}/${result.fileName}`
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
url: url,
|
|
271
|
+
rowCount: result.rowCount,
|
|
272
|
+
time: result.time,
|
|
273
|
+
fileName: result.fileName,
|
|
188
274
|
}
|
|
189
|
-
|
|
190
|
-
await this.service.delete(id)
|
|
191
|
-
reply.send({message: 'Item deleted successfully'})
|
|
275
|
+
|
|
192
276
|
} catch (e) {
|
|
193
277
|
console.error(e)
|
|
194
278
|
if (e instanceof ValidationError) {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {createDirIfNotExist} from "@drax/common-back";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
interface ExportOptions {
|
|
6
|
+
cursor: any
|
|
7
|
+
destinationPath: string
|
|
8
|
+
headers: string[] | string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type {ExportOptions}
|
|
12
|
+
|
|
13
|
+
class AbstractExport {
|
|
14
|
+
|
|
15
|
+
cursor: any
|
|
16
|
+
destinationPath: string
|
|
17
|
+
headers: string[]
|
|
18
|
+
|
|
19
|
+
fileName: string
|
|
20
|
+
relativeFilePath: string
|
|
21
|
+
|
|
22
|
+
constructor(options: ExportOptions) {
|
|
23
|
+
this.cursor = options.cursor;
|
|
24
|
+
this.destinationPath = options.destinationPath;
|
|
25
|
+
this.headers = Array.isArray(options.headers) ? options.headers : options.headers.split(',');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
createDirIfNotExist(): void {
|
|
30
|
+
createDirIfNotExist(this.destinationPath);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
generateFileName(extension:string): string {
|
|
34
|
+
if (!this.fileName) {
|
|
35
|
+
const randomUUID = crypto.randomUUID().toString();
|
|
36
|
+
this.fileName = `export_${randomUUID}.${extension}`;
|
|
37
|
+
}
|
|
38
|
+
return this.fileName;
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
generateFilePath(extension:string) {
|
|
43
|
+
this.createDirIfNotExist();
|
|
44
|
+
this.generateFileName(extension);
|
|
45
|
+
this.relativeFilePath = `${this.destinationPath}/${this.fileName}`;
|
|
46
|
+
return this.relativeFilePath;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
isIterableAsync(value: any) {
|
|
51
|
+
return value != null && typeof value[Symbol.asyncIterator] === 'function';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
isIterableSync(value: any) {
|
|
55
|
+
return value != null && typeof value[Symbol.iterator] === 'function';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getNestedProperty(obj: object, path: string) {
|
|
59
|
+
return path.split('.').reduce((acc, part) => {
|
|
60
|
+
if (Array.isArray(acc)) {
|
|
61
|
+
return acc.map(item => item[part]).join('|');
|
|
62
|
+
}
|
|
63
|
+
return acc && acc[part];
|
|
64
|
+
}, obj);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
AbstractExport,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default AbstractExport;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import AbstractExport from "./AbstractExport.js";
|
|
3
|
+
import type {ExportOptions} from "./AbstractExport";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
interface ExportCsvOptions extends ExportOptions {
|
|
7
|
+
separator: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class ExportCsv extends AbstractExport {
|
|
11
|
+
|
|
12
|
+
separator: string = ';'
|
|
13
|
+
|
|
14
|
+
constructor(options: ExportCsvOptions) {
|
|
15
|
+
super(options)
|
|
16
|
+
this.separator = options.separator ? options.separator : ';';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Método principal para procesar los datos y generar el CSV
|
|
20
|
+
process(): Promise<any> {
|
|
21
|
+
return new Promise(async (resolve, reject) => {
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
|
|
25
|
+
this.generateFilePath('csv')
|
|
26
|
+
let rowCount = 0
|
|
27
|
+
const start = Date.now();
|
|
28
|
+
|
|
29
|
+
const writableStream = fs.createWriteStream(this.relativeFilePath);
|
|
30
|
+
|
|
31
|
+
writableStream.on('error', reject);
|
|
32
|
+
writableStream.on('finish', () => resolve({
|
|
33
|
+
status: 'success',
|
|
34
|
+
destinationPath: this.destinationPath,
|
|
35
|
+
fileName: this.fileName,
|
|
36
|
+
filePath: this.destinationPath + '/' + this.fileName,
|
|
37
|
+
relativeFilePath: this.relativeFilePath,
|
|
38
|
+
rowCount: rowCount,
|
|
39
|
+
time: Date.now() - start,
|
|
40
|
+
message: 'Export successful',
|
|
41
|
+
}))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const csvHeaders = this.headers.join(this.separator);
|
|
45
|
+
writableStream.write(csvHeaders + '\n');
|
|
46
|
+
|
|
47
|
+
if (this.isIterableAsync(this.cursor)) {
|
|
48
|
+
for await (const record of this.cursor) {
|
|
49
|
+
const csvRow = this.convertRecordToCSVrow(record);
|
|
50
|
+
console.log("csvRow", csvRow);
|
|
51
|
+
writableStream.write(csvRow + '\n');
|
|
52
|
+
rowCount++
|
|
53
|
+
}
|
|
54
|
+
} else if (this.isIterableSync(this.cursor)) {
|
|
55
|
+
// Si es un cursor de SQLite (better-sqlite3), usamos iterate()
|
|
56
|
+
for (const record of this.cursor) {
|
|
57
|
+
const csvRow = this.convertRecordToCSVrow(record);
|
|
58
|
+
writableStream.write(csvRow + '\n');
|
|
59
|
+
rowCount++
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
writableStream.end();
|
|
63
|
+
} catch (e) {
|
|
64
|
+
reject(e);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
// Método que convierte un registro en una o más filas de CSV
|
|
72
|
+
convertRecordToCSVrow(record: object): string {
|
|
73
|
+
let fields = []
|
|
74
|
+
|
|
75
|
+
for (const header of this.headers) {
|
|
76
|
+
|
|
77
|
+
let value
|
|
78
|
+
if (header.includes('.')) {
|
|
79
|
+
value = this.getNestedProperty(record, header);
|
|
80
|
+
} else {
|
|
81
|
+
value = record[header];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (value === undefined) {
|
|
85
|
+
fields.push('');
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (Array.isArray(value)) {
|
|
90
|
+
if (value.length > 0 && typeof value[0] === 'object') {
|
|
91
|
+
fields.push(JSON.stringify(value));
|
|
92
|
+
} else {
|
|
93
|
+
fields.push(value.join(','));
|
|
94
|
+
}
|
|
95
|
+
} else if (typeof value === 'object') {
|
|
96
|
+
fields.push(JSON.stringify(value));
|
|
97
|
+
|
|
98
|
+
} else {
|
|
99
|
+
fields.push(value.toString());
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return fields.join(this.separator);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export default ExportCsv;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import AbstractExport from "./AbstractExport.js";
|
|
3
|
+
import type {ExportOptions} from "./AbstractExport";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ExportJson extends AbstractExport {
|
|
9
|
+
|
|
10
|
+
constructor(options: ExportOptions) {
|
|
11
|
+
super(options)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Método principal para procesar los datos y generar el CSV
|
|
15
|
+
process(): Promise<any> {
|
|
16
|
+
return new Promise(async (resolve, reject) => {
|
|
17
|
+
|
|
18
|
+
try{
|
|
19
|
+
this.generateFilePath('json')
|
|
20
|
+
let rowCount = 0
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
|
|
23
|
+
const writableStream = fs.createWriteStream(this.relativeFilePath);
|
|
24
|
+
|
|
25
|
+
writableStream.on('error', reject);
|
|
26
|
+
writableStream.on('finish', () => resolve({
|
|
27
|
+
status: 'success',
|
|
28
|
+
destinationPath: this.destinationPath,
|
|
29
|
+
fileName: this.fileName,
|
|
30
|
+
filePath: this.destinationPath + '/' + this.fileName,
|
|
31
|
+
relativeFilePath: this.relativeFilePath,
|
|
32
|
+
rowCount: rowCount,
|
|
33
|
+
time: Date.now() - start,
|
|
34
|
+
message: 'Export successful',
|
|
35
|
+
}))
|
|
36
|
+
|
|
37
|
+
writableStream.write('[');
|
|
38
|
+
|
|
39
|
+
if (this.isIterableAsync(this.cursor)) {
|
|
40
|
+
for await (const record of this.cursor) {
|
|
41
|
+
const csvRow = JSON.stringify(record);
|
|
42
|
+
console.log("csvRow", csvRow);
|
|
43
|
+
writableStream.write(csvRow + ',\n');
|
|
44
|
+
rowCount++
|
|
45
|
+
}
|
|
46
|
+
} else if (this.isIterableSync(this.cursor)) {
|
|
47
|
+
for (const record of this.cursor) {
|
|
48
|
+
const csvRow = JSON.stringify(record);
|
|
49
|
+
writableStream.write(csvRow + ',\n');
|
|
50
|
+
rowCount++
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
writableStream.write(']');
|
|
54
|
+
writableStream.end();
|
|
55
|
+
}catch (e){
|
|
56
|
+
reject(e);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default ExportJson;
|
package/src/index.ts
CHANGED
|
@@ -4,9 +4,6 @@ import AbstractSqliteRepository from "./repository/AbstractSqliteRepository.js";
|
|
|
4
4
|
import AbstractService from "./services/AbstractService.js";
|
|
5
5
|
import AbstractFastifyController from "./controllers/AbstractFastifyController.js";
|
|
6
6
|
|
|
7
|
-
import type {ICrudRepository} from "./interfaces/ICrudRepository";
|
|
8
|
-
|
|
9
|
-
export type {ICrudRepository}
|
|
10
7
|
|
|
11
8
|
export {
|
|
12
9
|
|
|
@@ -2,13 +2,11 @@ import "mongoose-paginate-v2";
|
|
|
2
2
|
import mongoose from "mongoose";
|
|
3
3
|
import {MongooseQueryFilter, MongooseSort, MongooseErrorToValidationError} from "@drax/common-back";
|
|
4
4
|
import type {DeleteResult} from "mongodb";
|
|
5
|
-
import type {IDraxPaginateOptions, IDraxPaginateResult} from "@drax/
|
|
5
|
+
import type {IDraxPaginateOptions, IDraxPaginateResult, IDraxFindOptions, IDraxCrud} from "@drax/crud-share";
|
|
6
6
|
import type {PaginateModel, PaginateOptions, PaginateResult} from "mongoose";
|
|
7
|
-
import type {ICrudRepository} from "../interfaces/ICrudRepository";
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
class AbstractMongoRepository<T,C,U> implements ICrudRepository<T,C,U> {
|
|
9
|
+
class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
12
10
|
|
|
13
11
|
_model: mongoose.Model<T> & PaginateModel<T>
|
|
14
12
|
_searchFields: string[] = []
|
|
@@ -76,7 +74,7 @@ class AbstractMongoRepository<T,C,U> implements ICrudRepository<T,C,U> {
|
|
|
76
74
|
return items
|
|
77
75
|
}
|
|
78
76
|
|
|
79
|
-
async search(value: string, limit
|
|
77
|
+
async search(value: string, limit: number = 1000): Promise<T[]> {
|
|
80
78
|
const query = {}
|
|
81
79
|
query['$or'] = this._searchFields.map(field => ({[field]: new RegExp(value, 'i')}))
|
|
82
80
|
const items: mongoose.HydratedDocument<T>[] = await this._model.find(query).limit(limit).exec()
|
|
@@ -112,6 +110,28 @@ class AbstractMongoRepository<T,C,U> implements ICrudRepository<T,C,U> {
|
|
|
112
110
|
items: items.docs
|
|
113
111
|
}
|
|
114
112
|
}
|
|
113
|
+
|
|
114
|
+
async find({
|
|
115
|
+
limit = 0,
|
|
116
|
+
orderBy = '',
|
|
117
|
+
order = false,
|
|
118
|
+
search = '',
|
|
119
|
+
filters = []
|
|
120
|
+
}: IDraxFindOptions): Promise<T[]> {
|
|
121
|
+
|
|
122
|
+
const query = {}
|
|
123
|
+
|
|
124
|
+
if (search) {
|
|
125
|
+
query['$or'] = this._searchFields.map(field => ({[field]: new RegExp(search, 'i')}))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
MongooseQueryFilter.applyFilters(query, filters)
|
|
129
|
+
|
|
130
|
+
const sort = MongooseSort.applySort(orderBy, order)
|
|
131
|
+
const populate = this._populateFields
|
|
132
|
+
const items: T[] = await this._model.find(query).sort(sort).populate(populate)
|
|
133
|
+
return items
|
|
134
|
+
}
|
|
115
135
|
}
|
|
116
136
|
|
|
117
137
|
export default AbstractMongoRepository
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import sqlite from "better-sqlite3";
|
|
2
|
-
import type {IDraxPaginateOptions, IDraxPaginateResult} from "@drax/
|
|
2
|
+
import type {IDraxPaginateOptions, IDraxPaginateResult} from "@drax/crud-share";
|
|
3
3
|
import {randomUUID} from "node:crypto";
|
|
4
4
|
import {
|
|
5
5
|
SqlSort, SqlQueryFilter, SqliteTableBuilder, SqliteTableField,
|