@drax/crud-back 0.36.0 → 0.37.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builders/CrudSchemaBuilder.js +25 -1
- package/dist/controllers/AbstractFastifyController.js +24 -1
- package/dist/index.js +2 -1
- package/dist/repository/AbstractMongoRepository.js +145 -0
- package/dist/schemas/GroupBySchema.js +7 -0
- package/dist/services/AbstractService.js +3 -0
- package/package.json +7 -7
- package/src/builders/CrudSchemaBuilder.ts +34 -1
- package/src/controllers/AbstractFastifyController.ts +32 -1
- package/src/index.ts +2 -0
- package/src/repository/AbstractMongoRepository.ts +196 -26
- package/src/schemas/GroupBySchema.ts +10 -0
- package/src/services/AbstractService.ts +5 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/types/builders/CrudSchemaBuilder.d.ts +177 -0
- package/types/builders/CrudSchemaBuilder.d.ts.map +1 -1
- package/types/controllers/AbstractFastifyController.d.ts +3 -0
- package/types/controllers/AbstractFastifyController.d.ts.map +1 -1
- package/types/index.d.ts +2 -1
- package/types/index.d.ts.map +1 -1
- package/types/repository/AbstractMongoRepository.d.ts +2 -1
- package/types/repository/AbstractMongoRepository.d.ts.map +1 -1
- package/types/schemas/GroupBySchema.d.ts +13 -0
- package/types/schemas/GroupBySchema.d.ts.map +1 -0
- package/types/services/AbstractService.d.ts +2 -1
- package/types/services/AbstractService.d.ts.map +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import z from 'zod';
|
|
2
2
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
3
|
-
import { IdParamSchema, DeleteBodyResponseSchema, PaginateQuerySchema, PaginateBodyResponseSchema, FindQuerySchema, SearchQuerySchema, FindByParamSchema, ErrorBodyResponseSchema, ValidationErrorBodyResponseSchema, ExportBodyResponseSchema } from '../index.js';
|
|
3
|
+
import { IdParamSchema, DeleteBodyResponseSchema, PaginateQuerySchema, PaginateBodyResponseSchema, FindQuerySchema, SearchQuerySchema, FindByParamSchema, ErrorBodyResponseSchema, ValidationErrorBodyResponseSchema, ExportBodyResponseSchema, GroupByQuerySchema } from '../index.js';
|
|
4
4
|
export class CrudSchemaBuilder {
|
|
5
5
|
constructor(entitySchema, entityCreateSchema, entityUpdateSchema, entityName, target = 'openApi3', tags = []) {
|
|
6
6
|
this.target = 'openApi3'; //"jsonSchema7" | "jsonSchema2019-09" | "openApi3" | "openAi"
|
|
@@ -29,6 +29,11 @@ export class CrudSchemaBuilder {
|
|
|
29
29
|
get jsonEntityArraySchema() {
|
|
30
30
|
return zodToJsonSchema(z.array(this.entitySchema), { target: this.target });
|
|
31
31
|
}
|
|
32
|
+
get jsonEntityGroupBySchema() {
|
|
33
|
+
return zodToJsonSchema(z.array(z.object({
|
|
34
|
+
count: z.number()
|
|
35
|
+
}).catchall(z.any())), { target: this.target });
|
|
36
|
+
}
|
|
32
37
|
get jsonExportBodyResponse() {
|
|
33
38
|
return zodToJsonSchema(ExportBodyResponseSchema, { target: this.target });
|
|
34
39
|
}
|
|
@@ -41,6 +46,9 @@ export class CrudSchemaBuilder {
|
|
|
41
46
|
get jsonFindQuerySchema() {
|
|
42
47
|
return zodToJsonSchema(FindQuerySchema, { target: this.target });
|
|
43
48
|
}
|
|
49
|
+
get jsonGroupByQuerySchema() {
|
|
50
|
+
return zodToJsonSchema(GroupByQuerySchema, { target: this.target });
|
|
51
|
+
}
|
|
44
52
|
get jsonSearchQuerySchema() {
|
|
45
53
|
return zodToJsonSchema(SearchQuerySchema, { target: this.target });
|
|
46
54
|
}
|
|
@@ -127,6 +135,22 @@ export class CrudSchemaBuilder {
|
|
|
127
135
|
}
|
|
128
136
|
};
|
|
129
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Get JSON schema for find entities
|
|
140
|
+
*/
|
|
141
|
+
get groupBySchema() {
|
|
142
|
+
return {
|
|
143
|
+
...(this.getTags),
|
|
144
|
+
query: this.jsonGroupByQuerySchema,
|
|
145
|
+
response: {
|
|
146
|
+
200: this.jsonEntityGroupBySchema,
|
|
147
|
+
400: this.jsonErrorBodyResponse,
|
|
148
|
+
401: this.jsonErrorBodyResponse,
|
|
149
|
+
403: this.jsonErrorBodyResponse,
|
|
150
|
+
500: this.jsonErrorBodyResponse
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
130
154
|
/**
|
|
131
155
|
* Get JSON schema for find entities
|
|
132
156
|
*/
|
|
@@ -332,7 +332,7 @@ class AbstractFastifyController extends CommonController {
|
|
|
332
332
|
const search = request.query.search;
|
|
333
333
|
const filters = this.parseFilters(request.query.filters);
|
|
334
334
|
this.applyUserAndTenantFilters(filters, request.rbac);
|
|
335
|
-
//console.log("
|
|
335
|
+
// console.log("paginate filters",filters)
|
|
336
336
|
let paginateResult = await this.service.paginate({ page, limit, orderBy, order, search, filters });
|
|
337
337
|
return paginateResult;
|
|
338
338
|
}
|
|
@@ -382,6 +382,29 @@ class AbstractFastifyController extends CommonController {
|
|
|
382
382
|
this.handleError(e, reply);
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
|
+
async groupBy(request, reply) {
|
|
386
|
+
try {
|
|
387
|
+
request.rbac.assertPermission(this.permission.View);
|
|
388
|
+
const fields = request.query.fields ?
|
|
389
|
+
request.query.fields.split(',').map(f => f.trim()).filter(f => f.length > 0) :
|
|
390
|
+
[];
|
|
391
|
+
const dateFormat = request.query.dateFormat ? request.query.dateFormat : 'day';
|
|
392
|
+
if (fields.length === 0) {
|
|
393
|
+
throw new BadRequestError('At least one field is required for grouping');
|
|
394
|
+
}
|
|
395
|
+
const filters = this.parseFilters(request.query.filters);
|
|
396
|
+
this.applyUserAndTenantFilters(filters, request.rbac);
|
|
397
|
+
const result = await this.service.groupBy({ fields, filters, dateFormat });
|
|
398
|
+
// console.log("groupby fields",fields)
|
|
399
|
+
// console.log("groupby dateFormat",dateFormat)
|
|
400
|
+
// console.log("groupby filters",filters)
|
|
401
|
+
// console.log("groupby result",result)
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
catch (e) {
|
|
405
|
+
this.handleError(e, reply);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
385
408
|
}
|
|
386
409
|
export default AbstractFastifyController;
|
|
387
410
|
export { AbstractFastifyController };
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { PaginateBodyResponseSchema, PaginateQuerySchema } from "./schemas/Pagin
|
|
|
10
10
|
import { FindQuerySchema } from "./schemas/FindSchema.js";
|
|
11
11
|
import { SearchQuerySchema } from "./schemas/SearchSchema.js";
|
|
12
12
|
import { FindByParamSchema } from "./schemas/FindBySchema.js";
|
|
13
|
+
import { GroupByQuerySchema } from "./schemas/GroupBySchema.js";
|
|
13
14
|
import { ExportBodyResponseSchema } from "./schemas/ExportBodyResponseSchema.js";
|
|
14
15
|
import { ErrorBodyResponseSchema, ValidationErrorBodyResponseSchema } from "./schemas/ErrorBodyResponseSchema.js";
|
|
15
16
|
import { CrudSchemaBuilder } from "./builders/CrudSchemaBuilder.js";
|
|
@@ -17,6 +18,6 @@ export {
|
|
|
17
18
|
//CRUD
|
|
18
19
|
AbstractMongoRepository, AbstractSqliteRepository, AbstractService, AbstractFastifyController,
|
|
19
20
|
//Schemas
|
|
20
|
-
IdParamSchema, DeleteBodyResponseSchema, PaginateBodyResponseSchema, PaginateQuerySchema, FindQuerySchema, SearchQuerySchema, FindByParamSchema, ErrorBodyResponseSchema, ValidationErrorBodyResponseSchema, ExportBodyResponseSchema,
|
|
21
|
+
IdParamSchema, DeleteBodyResponseSchema, PaginateBodyResponseSchema, PaginateQuerySchema, FindQuerySchema, GroupByQuerySchema, SearchQuerySchema, FindByParamSchema, ErrorBodyResponseSchema, ValidationErrorBodyResponseSchema, ExportBodyResponseSchema,
|
|
21
22
|
//Builder
|
|
22
23
|
CrudSchemaBuilder, };
|
|
@@ -215,6 +215,151 @@ class AbstractMongoRepository {
|
|
|
215
215
|
const sort = MongooseSort.applySort(orderBy, order);
|
|
216
216
|
return this._model.find(query).limit(limit).sort(sort).cursor();
|
|
217
217
|
}
|
|
218
|
+
async groupBy({ fields = [], filters = [], dateFormat = 'day' }) {
|
|
219
|
+
const query = {};
|
|
220
|
+
MongooseQueryFilter.applyFilters(query, filters);
|
|
221
|
+
// Obtener el schema para identificar campos de referencia y fechas
|
|
222
|
+
const schema = this._model.schema;
|
|
223
|
+
// Construir el objeto de agrupación dinámicamente
|
|
224
|
+
const groupId = {};
|
|
225
|
+
const lookupStages = [];
|
|
226
|
+
const finalProjectFields = { count: 1, _id: 0 };
|
|
227
|
+
const refFields = new Set();
|
|
228
|
+
const dateFields = new Set();
|
|
229
|
+
// Función para obtener el formato de fecha según el nivel de granularidad
|
|
230
|
+
const getDateFormat = (field, format) => {
|
|
231
|
+
const formats = {
|
|
232
|
+
'year': {
|
|
233
|
+
$dateFromParts: {
|
|
234
|
+
year: { $year: `$${field}` },
|
|
235
|
+
month: 1,
|
|
236
|
+
day: 1
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
'month': {
|
|
240
|
+
$dateFromParts: {
|
|
241
|
+
year: { $year: `$${field}` },
|
|
242
|
+
month: { $month: `$${field}` },
|
|
243
|
+
day: 1
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
'day': {
|
|
247
|
+
$dateFromParts: {
|
|
248
|
+
year: { $year: `$${field}` },
|
|
249
|
+
month: { $month: `$${field}` },
|
|
250
|
+
day: { $dayOfMonth: `$${field}` }
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
'hour': {
|
|
254
|
+
$dateFromParts: {
|
|
255
|
+
year: { $year: `$${field}` },
|
|
256
|
+
month: { $month: `$${field}` },
|
|
257
|
+
day: { $dayOfMonth: `$${field}` },
|
|
258
|
+
hour: { $hour: `$${field}` }
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
'minute': {
|
|
262
|
+
$dateFromParts: {
|
|
263
|
+
year: { $year: `$${field}` },
|
|
264
|
+
month: { $month: `$${field}` },
|
|
265
|
+
day: { $dayOfMonth: `$${field}` },
|
|
266
|
+
hour: { $hour: `$${field}` },
|
|
267
|
+
minute: { $minute: `$${field}` }
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
'second': {
|
|
271
|
+
$dateFromParts: {
|
|
272
|
+
year: { $year: `$${field}` },
|
|
273
|
+
month: { $month: `$${field}` },
|
|
274
|
+
day: { $dayOfMonth: `$${field}` },
|
|
275
|
+
hour: { $hour: `$${field}` },
|
|
276
|
+
minute: { $minute: `$${field}` },
|
|
277
|
+
second: { $second: `$${field}` }
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
return formats[format] || formats['day'];
|
|
282
|
+
};
|
|
283
|
+
fields.forEach(field => {
|
|
284
|
+
const schemaPath = schema.path(field);
|
|
285
|
+
// Verificar si el campo es de tipo Date
|
|
286
|
+
if (schemaPath && schemaPath.instance === 'Date') {
|
|
287
|
+
dateFields.add(field);
|
|
288
|
+
groupId[field] = getDateFormat(field, dateFormat);
|
|
289
|
+
}
|
|
290
|
+
// Verificar si el campo es una referencia
|
|
291
|
+
else if (schemaPath && schemaPath.options && schemaPath.options.ref) {
|
|
292
|
+
const refModel = schemaPath.options.ref;
|
|
293
|
+
const fieldName = field;
|
|
294
|
+
refFields.add(field);
|
|
295
|
+
// Obtener el modelo referenciado y su nombre de colección real
|
|
296
|
+
const refModelInstance = mongoose.model(refModel);
|
|
297
|
+
const collectionName = refModelInstance.collection.name;
|
|
298
|
+
// Determinar el campo local correcto según si es un solo campo o múltiples
|
|
299
|
+
const localField = fields.length === 1 ? '_id' : `_id.${fieldName}`;
|
|
300
|
+
lookupStages.push({
|
|
301
|
+
$lookup: {
|
|
302
|
+
from: collectionName,
|
|
303
|
+
localField: localField,
|
|
304
|
+
foreignField: '_id',
|
|
305
|
+
as: `${fieldName}_populated`
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
// Unwind para convertir el array en objeto único
|
|
309
|
+
lookupStages.push({
|
|
310
|
+
$unwind: {
|
|
311
|
+
path: `$${fieldName}_populated`,
|
|
312
|
+
preserveNullAndEmptyArrays: true
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
// En la proyección final, usar el objeto poblado
|
|
316
|
+
finalProjectFields[field] = `$${fieldName}_populated`;
|
|
317
|
+
groupId[field] = `$${field}`;
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
// Si no es una referencia ni fecha, usar el valor directo
|
|
321
|
+
groupId[field] = `$${field}`;
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
// Construir la proyección final para campos de fecha
|
|
325
|
+
fields.forEach(field => {
|
|
326
|
+
if (dateFields.has(field)) {
|
|
327
|
+
if (fields.length === 1) {
|
|
328
|
+
finalProjectFields[field] = `$_id`;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
finalProjectFields[field] = `$_id.${field}`;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
else if (!refFields.has(field)) {
|
|
335
|
+
if (fields.length === 1) {
|
|
336
|
+
finalProjectFields[field] = `$_id`;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
finalProjectFields[field] = `$_id.${field}`;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
const pipeline = [
|
|
344
|
+
{ $match: query },
|
|
345
|
+
{
|
|
346
|
+
$group: {
|
|
347
|
+
_id: fields.length === 1 ? (dateFields.has(fields[0]) ? getDateFormat(fields[0], dateFormat) : `$${fields[0]}`) : groupId,
|
|
348
|
+
count: { $sum: 1 }
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
];
|
|
352
|
+
// Solo agregar lookups si hay campos de referencia
|
|
353
|
+
if (lookupStages.length > 0) {
|
|
354
|
+
pipeline.push(...lookupStages);
|
|
355
|
+
}
|
|
356
|
+
pipeline.push({
|
|
357
|
+
$project: finalProjectFields
|
|
358
|
+
}, { $sort: { count: -1 } });
|
|
359
|
+
console.log("pipeline", JSON.stringify(pipeline, null, 2));
|
|
360
|
+
const result = await this._model.aggregate(pipeline).exec();
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
218
363
|
}
|
|
219
364
|
export default AbstractMongoRepository;
|
|
220
365
|
export { AbstractMongoRepository };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
import QueryFilterRegex from "../regexs/QueryFilterRegex.js";
|
|
3
|
+
const GroupByQuerySchema = z.object({
|
|
4
|
+
fields: z.array(z.string()).min(1).max(10),
|
|
5
|
+
filters: z.string().regex(QueryFilterRegex).optional().describe("Format: field;operator;value|field;operator;value|..."),
|
|
6
|
+
});
|
|
7
|
+
export { GroupByQuerySchema };
|
|
@@ -200,6 +200,9 @@ class AbstractService {
|
|
|
200
200
|
throw e;
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
|
+
async groupBy({ fields = [], filters = [], dateFormat = 'day' }) {
|
|
204
|
+
return await this._repository.groupBy({ fields, filters, dateFormat });
|
|
205
|
+
}
|
|
203
206
|
async export({ format = 'JSON', headers = [], headersTranslate = [], separator = ';', fileName = 'export', orderBy = '', order = false, search = '', filters = [] }, destinationPath) {
|
|
204
207
|
try {
|
|
205
208
|
let cursor;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.37.2",
|
|
7
7
|
"description": "Crud utils across modules",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"author": "Cristian Incarnato & Drax Team",
|
|
23
23
|
"license": "ISC",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@drax/common-back": "^0.
|
|
26
|
-
"@drax/common-share": "^0.
|
|
27
|
-
"@drax/identity-share": "^0.
|
|
28
|
-
"@drax/media-back": "^0.
|
|
25
|
+
"@drax/common-back": "^0.37.2",
|
|
26
|
+
"@drax/common-share": "^0.37.0",
|
|
27
|
+
"@drax/identity-share": "^0.37.0",
|
|
28
|
+
"@drax/media-back": "^0.37.2",
|
|
29
29
|
"@graphql-tools/load-files": "^7.0.0",
|
|
30
30
|
"@graphql-tools/merge": "^9.0.4",
|
|
31
31
|
"mongoose": "^8.6.3",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"mongoose-paginate-v2": "^1.8.3"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"dayjs": "^1.11.
|
|
36
|
+
"dayjs": "^1.11.19",
|
|
37
37
|
"mongoose-paginate-v2": "^1.8.3"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"tsc-alias": "^1.8.10",
|
|
46
46
|
"typescript": "^5.6.2"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "85833fa79a0d1df3899c13a3a47ed3e7e3fe0448"
|
|
49
49
|
}
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
FindByParamSchema,
|
|
12
12
|
ErrorBodyResponseSchema,
|
|
13
13
|
ValidationErrorBodyResponseSchema,
|
|
14
|
-
ExportBodyResponseSchema
|
|
14
|
+
ExportBodyResponseSchema,
|
|
15
|
+
GroupByQuerySchema
|
|
15
16
|
} from '../index.js';
|
|
16
17
|
|
|
17
18
|
export class CrudSchemaBuilder<T extends z.ZodObject<z.ZodRawShape>, TCreate extends z.ZodObject<z.ZodRawShape>, TUpdate extends z.ZodObject<z.ZodRawShape>> {
|
|
@@ -54,6 +55,17 @@ export class CrudSchemaBuilder<T extends z.ZodObject<z.ZodRawShape>, TCreate ext
|
|
|
54
55
|
return zodToJsonSchema(z.array(this.entitySchema), {target: this.target})
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
get jsonEntityGroupBySchema() {
|
|
59
|
+
return zodToJsonSchema(
|
|
60
|
+
z.array(
|
|
61
|
+
z.object({
|
|
62
|
+
count: z.number()
|
|
63
|
+
}).catchall(z.any())
|
|
64
|
+
),
|
|
65
|
+
{target: this.target}
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
get jsonExportBodyResponse() {
|
|
58
70
|
return zodToJsonSchema(ExportBodyResponseSchema, {target: this.target})
|
|
59
71
|
}
|
|
@@ -70,6 +82,10 @@ export class CrudSchemaBuilder<T extends z.ZodObject<z.ZodRawShape>, TCreate ext
|
|
|
70
82
|
return zodToJsonSchema(FindQuerySchema, {target: this.target})
|
|
71
83
|
}
|
|
72
84
|
|
|
85
|
+
get jsonGroupByQuerySchema(){
|
|
86
|
+
return zodToJsonSchema(GroupByQuerySchema, {target: this.target})
|
|
87
|
+
}
|
|
88
|
+
|
|
73
89
|
get jsonSearchQuerySchema(){
|
|
74
90
|
return zodToJsonSchema(SearchQuerySchema, {target: this.target})
|
|
75
91
|
}
|
|
@@ -166,6 +182,23 @@ export class CrudSchemaBuilder<T extends z.ZodObject<z.ZodRawShape>, TCreate ext
|
|
|
166
182
|
}
|
|
167
183
|
}
|
|
168
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Get JSON schema for find entities
|
|
187
|
+
*/
|
|
188
|
+
get groupBySchema() {
|
|
189
|
+
return {
|
|
190
|
+
...(this.getTags),
|
|
191
|
+
query: this.jsonGroupByQuerySchema,
|
|
192
|
+
response: {
|
|
193
|
+
200: this.jsonEntityGroupBySchema,
|
|
194
|
+
400: this.jsonErrorBodyResponse,
|
|
195
|
+
401: this.jsonErrorBodyResponse,
|
|
196
|
+
403: this.jsonErrorBodyResponse,
|
|
197
|
+
500: this.jsonErrorBodyResponse
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
169
202
|
/**
|
|
170
203
|
* Get JSON schema for find entities
|
|
171
204
|
*/
|
|
@@ -39,6 +39,8 @@ type CustomRequest = FastifyRequest<{
|
|
|
39
39
|
headersTranslate?: string
|
|
40
40
|
separator?: string
|
|
41
41
|
fileName?: string
|
|
42
|
+
fields?: string
|
|
43
|
+
dateFormat?: 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'
|
|
42
44
|
}
|
|
43
45
|
}>
|
|
44
46
|
|
|
@@ -453,7 +455,7 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
453
455
|
const filters: IDraxFieldFilter[] = this.parseFilters(request.query.filters)
|
|
454
456
|
this.applyUserAndTenantFilters(filters, request.rbac);
|
|
455
457
|
|
|
456
|
-
//console.log("
|
|
458
|
+
// console.log("paginate filters",filters)
|
|
457
459
|
|
|
458
460
|
let paginateResult = await this.service.paginate({page, limit, orderBy, order, search, filters})
|
|
459
461
|
return paginateResult
|
|
@@ -513,6 +515,35 @@ class AbstractFastifyController<T, C, U> extends CommonController {
|
|
|
513
515
|
this.handleError(e, reply)
|
|
514
516
|
}
|
|
515
517
|
}
|
|
518
|
+
|
|
519
|
+
async groupBy(request: CustomRequest, reply: FastifyReply) {
|
|
520
|
+
try {
|
|
521
|
+
request.rbac.assertPermission(this.permission.View)
|
|
522
|
+
|
|
523
|
+
const fields: string[] = request.query.fields ?
|
|
524
|
+
request.query.fields.split(',').map(f => f.trim()).filter(f => f.length > 0) :
|
|
525
|
+
[]
|
|
526
|
+
|
|
527
|
+
const dateFormat = request.query.dateFormat ? request.query.dateFormat : 'day'
|
|
528
|
+
|
|
529
|
+
if (fields.length === 0) {
|
|
530
|
+
throw new BadRequestError('At least one field is required for grouping')
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const filters: IDraxFieldFilter[] = this.parseFilters(request.query.filters)
|
|
534
|
+
this.applyUserAndTenantFilters(filters, request.rbac)
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
const result = await this.service.groupBy({fields, filters, dateFormat})
|
|
538
|
+
// console.log("groupby fields",fields)
|
|
539
|
+
// console.log("groupby dateFormat",dateFormat)
|
|
540
|
+
// console.log("groupby filters",filters)
|
|
541
|
+
// console.log("groupby result",result)
|
|
542
|
+
return result
|
|
543
|
+
} catch (e) {
|
|
544
|
+
this.handleError(e, reply)
|
|
545
|
+
}
|
|
546
|
+
}
|
|
516
547
|
}
|
|
517
548
|
|
|
518
549
|
export default AbstractFastifyController;
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {PaginateBodyResponseSchema, PaginateQuerySchema} from "./schemas/Paginat
|
|
|
12
12
|
import {FindQuerySchema} from "./schemas/FindSchema.js"
|
|
13
13
|
import {SearchQuerySchema} from "./schemas/SearchSchema.js"
|
|
14
14
|
import {FindByParamSchema} from "./schemas/FindBySchema.js"
|
|
15
|
+
import {GroupByQuerySchema} from "./schemas/GroupBySchema.js"
|
|
15
16
|
import {ExportBodyResponseSchema} from "./schemas/ExportBodyResponseSchema.js"
|
|
16
17
|
import {ErrorBodyResponseSchema, ValidationErrorBodyResponseSchema} from "./schemas/ErrorBodyResponseSchema.js"
|
|
17
18
|
import {CrudSchemaBuilder} from "./builders/CrudSchemaBuilder.js";
|
|
@@ -35,6 +36,7 @@ export {
|
|
|
35
36
|
PaginateBodyResponseSchema,
|
|
36
37
|
PaginateQuerySchema,
|
|
37
38
|
FindQuerySchema,
|
|
39
|
+
GroupByQuerySchema,
|
|
38
40
|
SearchQuerySchema,
|
|
39
41
|
FindByParamSchema,
|
|
40
42
|
ErrorBodyResponseSchema,
|