@solidstarters/solid-core 1.2.72 → 1.2.75

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 (99) hide show
  1. package/dist/controllers/export-template.controller.d.ts +36 -0
  2. package/dist/controllers/export-template.controller.d.ts.map +1 -0
  3. package/dist/controllers/export-template.controller.js +182 -0
  4. package/dist/controllers/export-template.controller.js.map +1 -0
  5. package/dist/controllers/export-transaction.controller.d.ts +33 -0
  6. package/dist/controllers/export-transaction.controller.d.ts.map +1 -0
  7. package/dist/controllers/export-transaction.controller.js +150 -0
  8. package/dist/controllers/export-transaction.controller.js.map +1 -0
  9. package/dist/dtos/create-export-template.dto.d.ts +9 -0
  10. package/dist/dtos/create-export-template.dto.d.ts.map +1 -0
  11. package/dist/dtos/create-export-template.dto.js +55 -0
  12. package/dist/dtos/create-export-template.dto.js.map +1 -0
  13. package/dist/dtos/create-export-transaction.dto.d.ts +9 -0
  14. package/dist/dtos/create-export-transaction.dto.d.ts.map +1 -0
  15. package/dist/dtos/create-export-transaction.dto.js +50 -0
  16. package/dist/dtos/create-export-transaction.dto.js.map +1 -0
  17. package/dist/dtos/update-export-template.dto.d.ts +10 -0
  18. package/dist/dtos/update-export-template.dto.d.ts.map +1 -0
  19. package/dist/dtos/update-export-template.dto.js +62 -0
  20. package/dist/dtos/update-export-template.dto.js.map +1 -0
  21. package/dist/dtos/update-export-transaction.dto.d.ts +9 -0
  22. package/dist/dtos/update-export-transaction.dto.d.ts.map +1 -0
  23. package/dist/dtos/update-export-transaction.dto.js +53 -0
  24. package/dist/dtos/update-export-transaction.dto.js.map +1 -0
  25. package/dist/entities/export-template.entity.d.ts +10 -0
  26. package/dist/entities/export-template.entity.d.ts.map +1 -0
  27. package/dist/entities/export-template.entity.js +53 -0
  28. package/dist/entities/export-template.entity.js.map +1 -0
  29. package/dist/entities/export-transaction.entity.d.ts +10 -0
  30. package/dist/entities/export-transaction.entity.d.ts.map +1 -0
  31. package/dist/entities/export-transaction.entity.js +51 -0
  32. package/dist/entities/export-transaction.entity.js.map +1 -0
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/interfaces.d.ts +2 -0
  38. package/dist/interfaces.d.ts.map +1 -1
  39. package/dist/interfaces.js.map +1 -1
  40. package/dist/seeders/seed-data/solid-core-metadata.json +183 -1
  41. package/dist/services/csv.service.d.ts +6 -0
  42. package/dist/services/csv.service.d.ts.map +1 -0
  43. package/dist/services/csv.service.js +48 -0
  44. package/dist/services/csv.service.js.map +1 -0
  45. package/dist/services/excel.service.d.ts +6 -0
  46. package/dist/services/excel.service.d.ts.map +1 -0
  47. package/dist/services/excel.service.js +90 -0
  48. package/dist/services/excel.service.js.map +1 -0
  49. package/dist/services/export-template.service.d.ts +27 -0
  50. package/dist/services/export-template.service.d.ts.map +1 -0
  51. package/dist/services/export-template.service.js +79 -0
  52. package/dist/services/export-template.service.js.map +1 -0
  53. package/dist/services/export-transaction.service.d.ts +50 -0
  54. package/dist/services/export-transaction.service.d.ts.map +1 -0
  55. package/dist/services/export-transaction.service.js +191 -0
  56. package/dist/services/export-transaction.service.js.map +1 -0
  57. package/dist/services/file.service.d.ts +3 -0
  58. package/dist/services/file.service.d.ts.map +1 -1
  59. package/dist/services/file.service.js +18 -2
  60. package/dist/services/file.service.js.map +1 -1
  61. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts +4 -2
  62. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
  63. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +4 -1
  64. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  65. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts +5 -3
  66. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
  67. package/dist/services/mediaStorageProviders/file-storage-provider.js +23 -3
  68. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  69. package/dist/services/model-metadata.service.js +1 -1
  70. package/dist/services/model-metadata.service.js.map +1 -1
  71. package/dist/services/solid-introspect.service.d.ts.map +1 -1
  72. package/dist/services/solid-introspect.service.js.map +1 -1
  73. package/dist/solid-core.module.d.ts.map +1 -1
  74. package/dist/solid-core.module.js +17 -1
  75. package/dist/solid-core.module.js.map +1 -1
  76. package/dist/tsconfig.tsbuildinfo +1 -1
  77. package/package.json +3 -1
  78. package/src/controllers/export-template.controller.ts +98 -0
  79. package/src/controllers/export-transaction.controller.ts +76 -0
  80. package/src/dtos/create-export-template.dto.ts +27 -0
  81. package/src/dtos/create-export-transaction.dto.ts +26 -0
  82. package/src/dtos/update-export-template.dto.ts +33 -0
  83. package/src/dtos/update-export-transaction.dto.ts +28 -0
  84. package/src/entities/export-template.entity.ts +20 -0
  85. package/src/entities/export-transaction.entity.ts +22 -0
  86. package/src/index.ts +2 -0
  87. package/src/interfaces.ts +2 -0
  88. package/src/seeders/seed-data/solid-core-metadata.json +183 -1
  89. package/src/services/csv.service.ts +41 -0
  90. package/src/services/excel.service.ts +105 -0
  91. package/src/services/export-template.service.ts +71 -0
  92. package/src/services/export-transaction.service.ts +208 -0
  93. package/src/services/export_issues.txt +6 -0
  94. package/src/services/file.service.ts +26 -1
  95. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +9 -6
  96. package/src/services/mediaStorageProviders/file-storage-provider.ts +29 -7
  97. package/src/services/model-metadata.service.ts +1 -1
  98. package/src/services/solid-introspect.service.ts +1 -0
  99. package/src/solid-core.module.ts +18 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidstarters/solid-core",
3
- "version": "1.2.72",
3
+ "version": "1.2.75",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -44,6 +44,8 @@
44
44
  "cache-manager-redis-store": "^3.0.1",
45
45
  "class-transformer": "^0.5.1",
46
46
  "class-validator": "^0.14.1",
47
+ "exceljs": "^4.4.0",
48
+ "fast-csv": "^5.0.2",
47
49
  "handlebars": "^4.7.8",
48
50
  "ioredis": "^5.4.1",
49
51
  "lodash": "^4.17.21",
@@ -0,0 +1,98 @@
1
+ import { Controller, Post, Body, Param, UploadedFiles, UseInterceptors, Put, Get, Query, Delete, Patch, Res, InternalServerErrorException } from '@nestjs/common';
2
+ import { AnyFilesInterceptor } from "@nestjs/platform-express";
3
+ import { ApiBearerAuth, ApiQuery, ApiTags } from '@nestjs/swagger';
4
+ import { ExportTemplateService } from '../services/export-template.service';
5
+ import { CreateExportTemplateDto } from '../dtos/create-export-template.dto';
6
+ import { UpdateExportTemplateDto } from '../dtos/update-export-template.dto';
7
+ import { Response } from 'express';
8
+
9
+ @ApiTags('Solid')
10
+ @Controller('export-template') //FIXME: Change this to the model plural name
11
+ export class ExportTemplateController {
12
+ constructor(private readonly service: ExportTemplateService) {}
13
+
14
+ @ApiBearerAuth("jwt")
15
+ @Post()
16
+ @UseInterceptors(AnyFilesInterceptor())
17
+ create(@Body() createDto: CreateExportTemplateDto, @UploadedFiles() files: Array<Express.Multer.File>) {
18
+ return this.service.create(createDto, files);
19
+ }
20
+
21
+ @ApiBearerAuth("jwt")
22
+ @Post('/bulk')
23
+ @UseInterceptors(AnyFilesInterceptor())
24
+ insertMany(@Body() createDtos: CreateExportTemplateDto[], @UploadedFiles() filesArray: Express.Multer.File[][] = []) {
25
+ return this.service.insertMany(createDtos, filesArray);
26
+ }
27
+
28
+
29
+ @ApiBearerAuth("jwt")
30
+ @Put(':id')
31
+ @UseInterceptors(AnyFilesInterceptor())
32
+ update(@Param('id') id: number, @Body() updateDto: UpdateExportTemplateDto, @UploadedFiles() files: Array<Express.Multer.File>) {
33
+ return this.service.update(id, updateDto, files);
34
+ }
35
+
36
+ @ApiBearerAuth("jwt")
37
+ @Patch(':id')
38
+ @UseInterceptors(AnyFilesInterceptor())
39
+ partialUpdate(@Param('id') id: number, @Body() updateDto: UpdateExportTemplateDto, @UploadedFiles() files: Array<Express.Multer.File>) {
40
+ return this.service.update(id, updateDto, files, true);
41
+ }
42
+
43
+ @ApiBearerAuth("jwt")
44
+ @ApiQuery({ name: 'showSoftDeleted', required: false, type: Boolean })
45
+ @ApiQuery({ name: 'showOnlySoftDeleted', required: false, type: Boolean })
46
+ @ApiQuery({ name: 'limit', required: false, type: Number })
47
+ @ApiQuery({ name: 'offset', required: false, type: Number })
48
+ @ApiQuery({ name: 'fields', required: false, type: Array })
49
+ @ApiQuery({ name: 'sort', required: false, type: Array })
50
+ @ApiQuery({ name: 'groupBy', required: false, type: Array })
51
+ @ApiQuery({ name: 'populate', required: false, type: Array })
52
+ @ApiQuery({ name: 'populateMedia', required: false, type: Array })
53
+ @ApiQuery({ name: 'filters', required: false, type: Array })
54
+ @Get()
55
+ async findMany(@Query() query: any) {
56
+ return this.service.find(query);
57
+ }
58
+
59
+ @ApiBearerAuth("jwt")
60
+ @Get(':id')
61
+ async findOne(@Param('id') id: string, @Query() query: any) {
62
+ return this.service.findOne(+id, query);
63
+ }
64
+
65
+ @Delete('/bulk')
66
+ async deleteMany(@Body() ids: number[]) {
67
+ return this.service.deleteMany(ids);
68
+ }
69
+
70
+ @ApiBearerAuth("jwt")
71
+ @Delete(':id')
72
+ async delete(@Param('id') id: number) {
73
+ return this.service.delete(id);
74
+ }
75
+
76
+ @ApiBearerAuth("jwt")
77
+ @Post(':id/startExport/sync')
78
+ async startExportSync(@Param('id') id: number, @Res() res: Response) {
79
+ const exportFileInfo = await this.service.startExportSync(+id);
80
+ if (exportFileInfo.exportStream === null) {
81
+ throw new InternalServerErrorException("Export stream is null");
82
+ }
83
+
84
+ // ✅ Set response headers for streaming
85
+ res.setHeader('Content-Disposition', `attachment; filename="${exportFileInfo.fileName}"`);
86
+ res.setHeader('Content-Type', exportFileInfo.mimeType);
87
+ res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition, Content-Type');
88
+ // Pipe the strea to the response as an excel file
89
+ exportFileInfo.exportStream.pipe(res);
90
+ }
91
+
92
+ @ApiBearerAuth("jwt")
93
+ @Post(':id/startExport/async')
94
+ async startExportAsync(@Param('id') id: number) {
95
+ return this.service.startExportAsync(+id);
96
+ }
97
+
98
+ }
@@ -0,0 +1,76 @@
1
+ import { Controller, Post, Body, Param, UploadedFiles, UseInterceptors, Put, Get, Query, Delete, Patch } from '@nestjs/common';
2
+ import { AnyFilesInterceptor } from "@nestjs/platform-express";
3
+ import { ApiBearerAuth, ApiQuery, ApiTags } from '@nestjs/swagger';
4
+ import { ExportTransactionService } from '../services/export-transaction.service';
5
+ import { CreateExportTransactionDto } from '../dtos/create-export-transaction.dto';
6
+ import { UpdateExportTransactionDto } from '../dtos/update-export-transaction.dto';
7
+
8
+ @ApiTags('Solid')
9
+ @Controller('export-transaction') //FIXME: Change this to the model plural name
10
+ export class ExportTransactionController {
11
+ constructor(private readonly service: ExportTransactionService) {}
12
+
13
+ @ApiBearerAuth("jwt")
14
+ @Post()
15
+ @UseInterceptors(AnyFilesInterceptor())
16
+ create(@Body() createDto: CreateExportTransactionDto, @UploadedFiles() files: Array<Express.Multer.File>) {
17
+ return this.service.create(createDto, files);
18
+ }
19
+
20
+ @ApiBearerAuth("jwt")
21
+ @Post('/bulk')
22
+ @UseInterceptors(AnyFilesInterceptor())
23
+ insertMany(@Body() createDtos: CreateExportTransactionDto[], @UploadedFiles() filesArray: Express.Multer.File[][] = []) {
24
+ return this.service.insertMany(createDtos, filesArray);
25
+ }
26
+
27
+
28
+ @ApiBearerAuth("jwt")
29
+ @Put(':id')
30
+ @UseInterceptors(AnyFilesInterceptor())
31
+ update(@Param('id') id: number, @Body() updateDto: UpdateExportTransactionDto, @UploadedFiles() files: Array<Express.Multer.File>) {
32
+ return this.service.update(id, updateDto, files);
33
+ }
34
+
35
+ @ApiBearerAuth("jwt")
36
+ @Patch(':id')
37
+ @UseInterceptors(AnyFilesInterceptor())
38
+ partialUpdate(@Param('id') id: number, @Body() updateDto: UpdateExportTransactionDto, @UploadedFiles() files: Array<Express.Multer.File>) {
39
+ return this.service.update(id, updateDto, files, true);
40
+ }
41
+
42
+ @ApiBearerAuth("jwt")
43
+ @ApiQuery({ name: 'showSoftDeleted', required: false, type: Boolean })
44
+ @ApiQuery({ name: 'showOnlySoftDeleted', required: false, type: Boolean })
45
+ @ApiQuery({ name: 'limit', required: false, type: Number })
46
+ @ApiQuery({ name: 'offset', required: false, type: Number })
47
+ @ApiQuery({ name: 'fields', required: false, type: Array })
48
+ @ApiQuery({ name: 'sort', required: false, type: Array })
49
+ @ApiQuery({ name: 'groupBy', required: false, type: Array })
50
+ @ApiQuery({ name: 'populate', required: false, type: Array })
51
+ @ApiQuery({ name: 'populateMedia', required: false, type: Array })
52
+ @ApiQuery({ name: 'filters', required: false, type: Array })
53
+ @Get()
54
+ async findMany(@Query() query: any) {
55
+ return this.service.find(query);
56
+ }
57
+
58
+ @ApiBearerAuth("jwt")
59
+ @Get(':id')
60
+ async findOne(@Param('id') id: string, @Query() query: any) {
61
+ return this.service.findOne(+id, query);
62
+ }
63
+
64
+ @Delete('/bulk')
65
+ async deleteMany(@Body() ids: number[]) {
66
+ return this.service.deleteMany(ids);
67
+ }
68
+
69
+ @ApiBearerAuth("jwt")
70
+ @Delete(':id')
71
+ async delete(@Param('id') id: number) {
72
+ return this.service.delete(id);
73
+ }
74
+
75
+
76
+ }
@@ -0,0 +1,27 @@
1
+ import { IsString } from 'class-validator';
2
+ import { IsNotEmpty, IsBoolean, IsOptional, IsInt, IsJSON } from 'class-validator';
3
+ export class CreateExportTemplateDto {
4
+ @IsNotEmpty()
5
+ @IsString()
6
+ templateName: string;
7
+
8
+ @IsNotEmpty()
9
+ @IsString()
10
+ templateFormat: string;
11
+
12
+ @IsOptional()
13
+ @IsBoolean()
14
+ notifyOnEmail: boolean = true;
15
+
16
+ @IsNotEmpty()
17
+ @IsJSON()
18
+ fields: any;
19
+
20
+ @IsOptional()
21
+ @IsInt()
22
+ modelMetadataId: number;
23
+
24
+ @IsString()
25
+ @IsOptional()
26
+ modelMetadataUserKey: string;
27
+ }
@@ -0,0 +1,26 @@
1
+ import { IsDate } from 'class-validator';
2
+ import { IsNotEmpty, IsString, IsOptional, IsInt } from 'class-validator';
3
+ export class CreateExportTransactionDto {
4
+ constructor(data: Partial<CreateExportTransactionDto>) {
5
+ Object.assign(this, data);
6
+ }
7
+ @IsNotEmpty()
8
+ @IsDate()
9
+ datetime: Date;
10
+
11
+ @IsNotEmpty()
12
+ @IsString()
13
+ status: string;
14
+
15
+ @IsOptional()
16
+ @IsString()
17
+ error: string;
18
+
19
+ @IsOptional()
20
+ @IsInt()
21
+ exportTemplateId: number;
22
+
23
+ @IsString()
24
+ @IsOptional()
25
+ exportTemplateUserKey: string;
26
+ }
@@ -0,0 +1,33 @@
1
+ import { IsInt,IsOptional, IsString, IsNotEmpty, IsBoolean, IsJSON } from 'class-validator';
2
+ export class UpdateExportTemplateDto {
3
+ @IsOptional()
4
+ @IsInt()
5
+ id: number;
6
+
7
+ @IsNotEmpty()
8
+ @IsOptional()
9
+ @IsString()
10
+ templateName: string;
11
+
12
+ @IsNotEmpty()
13
+ @IsOptional()
14
+ @IsString()
15
+ templateFormat: string;
16
+
17
+ @IsOptional()
18
+ @IsBoolean()
19
+ notifyOnEmail: boolean = true;
20
+
21
+ @IsNotEmpty()
22
+ @IsOptional()
23
+ @IsJSON()
24
+ fields: any;
25
+
26
+ @IsOptional()
27
+ @IsInt()
28
+ modelMetadataId: number;
29
+
30
+ @IsString()
31
+ @IsOptional()
32
+ modelMetadataUserKey: string;
33
+ }
@@ -0,0 +1,28 @@
1
+ import { IsInt,IsOptional, IsDate, IsNotEmpty, IsString } from 'class-validator';
2
+ export class UpdateExportTransactionDto {
3
+ @IsOptional()
4
+ @IsInt()
5
+ id: number;
6
+
7
+ @IsNotEmpty()
8
+ @IsOptional()
9
+ @IsDate()
10
+ datetime: Date;
11
+
12
+ @IsNotEmpty()
13
+ @IsOptional()
14
+ @IsString()
15
+ status: string;
16
+
17
+ @IsOptional()
18
+ @IsString()
19
+ error: string;
20
+
21
+ @IsOptional()
22
+ @IsInt()
23
+ exportTemplateId: number;
24
+
25
+ @IsString()
26
+ @IsOptional()
27
+ exportTemplateUserKey: string;
28
+ }
@@ -0,0 +1,20 @@
1
+ import { CommonEntity } from 'src/entities/common.entity'
2
+ import { Entity, Column, Index, ManyToOne } from 'typeorm';
3
+ import { ModelMetadata } from 'src/entities/model-metadata.entity'
4
+
5
+ @Entity("ss_export_template")
6
+ @Index(["templateName", "deletedTracker"], { unique: true })
7
+ export class ExportTemplate extends CommonEntity {
8
+ @Index()
9
+ @Column({ type: "varchar" })
10
+ templateName: string;
11
+ @Column({ type: "varchar" })
12
+ templateFormat: string;
13
+ @Column({ type: "boolean", nullable: true, default: true })
14
+ notifyOnEmail: boolean = true;
15
+ @Column({ type: "varchar" })
16
+ fields: any;
17
+ @Index()
18
+ @ManyToOne(() => ModelMetadata, { onDelete: "CASCADE", nullable: false })
19
+ modelMetadata: ModelMetadata;
20
+ }
@@ -0,0 +1,22 @@
1
+ import { CommonEntity } from 'src/entities/common.entity'
2
+ import { Entity, Column, Index, ManyToOne } from 'typeorm';
3
+ import { ExportTemplate } from 'src/entities/export-template.entity'
4
+
5
+ @Entity("ss_export_transaction")
6
+ @Index(["exportTransactionId", "deletedTracker"], { unique: true })
7
+ export class ExportTransaction extends CommonEntity {
8
+ @Index()
9
+ @Column({ type: "timestamp" })
10
+ datetime: Date;
11
+ @Index()
12
+ @Column({ type: "varchar" })
13
+ exportTransactionId: string;
14
+ @Index()
15
+ @Column({ type: "varchar" })
16
+ status: string;
17
+ @Column({ type: "text", nullable: true })
18
+ error: string;
19
+ @Index()
20
+ @ManyToOne(() => ExportTemplate, { onDelete: "CASCADE", nullable: false })
21
+ exportTemplate: ExportTemplate;
22
+ }
package/src/index.ts CHANGED
@@ -111,6 +111,8 @@ export * from './entities/security-rule.entity'
111
111
  export * from './entities/saved-filters.entity'
112
112
  export * from './entities/chatter-message.entity'
113
113
  export * from './entities/chatter-message-details.entity'
114
+ export * from './entities/export-template.entity'
115
+ export * from './entities/export-transaction.entity'
114
116
 
115
117
  export * from './enums/auth-type.enum'
116
118
 
package/src/interfaces.ts CHANGED
@@ -11,6 +11,7 @@ import { CreateSecurityRuleDto } from './dtos/create-security-rule.dto';
11
11
  import { CreateViewMetadataDto } from './dtos/create-view-metadata.dto';
12
12
  import { FieldMetadata } from './entities/field-metadata.entity';
13
13
  import { Media } from './entities/media.entity';
14
+ import { Readable } from 'stream';
14
15
 
15
16
  export interface FieldCrudManager {
16
17
  // fieldMetadata: FieldMetadata;
@@ -34,6 +35,7 @@ export interface MediaStorageProvider<T> {
34
35
  store(files: Express.Multer.File[], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]>;
35
36
  delete(entity: T, mediaFieldMetadata: FieldMetadata): Promise<void>;
36
37
  retrieve(entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]>;
38
+ storeStreams(streamPairs: [Readable, string][], entity: T, mediaFieldMetadata: FieldMetadata): Promise<Media[]>;
37
39
  // delete(file: string): Promise<void>;
38
40
  }
39
41
 
@@ -2828,7 +2828,7 @@
2828
2828
  "private": false,
2829
2829
  "encrypt": false,
2830
2830
  "relationType": "many-to-one",
2831
- "relationModelSingularName": "emailTemplate",
2831
+ "relationCoModelSingularName": "emailTemplate",
2832
2832
  "relationCreateInverse": false,
2833
2833
  "relationCascade": "cascade",
2834
2834
  "relationTypeManyToManyOwner": true,
@@ -3395,6 +3395,188 @@
3395
3395
  "isRelationManyToManyOwner": null
3396
3396
  }
3397
3397
  ]
3398
+ },
3399
+ {
3400
+ "singularName": "exportTemplate",
3401
+ "tableName": "ss_export_template",
3402
+ "pluralName": "exportTemplates",
3403
+ "displayName": "Export Templates",
3404
+ "description": "Model to capture all information related to an file export template",
3405
+ "dataSource": "default",
3406
+ "dataSourceType": "postgres",
3407
+ "isSystem": true,
3408
+ "userKeyFieldUserKey": "templateName",
3409
+ "fields": [
3410
+ {
3411
+ "name": "templateName",
3412
+ "displayName": "Template Name",
3413
+ "type": "shortText",
3414
+ "ormType": "varchar",
3415
+ "length": 256,
3416
+ "required": true,
3417
+ "unique": true,
3418
+ "index": true,
3419
+ "private": false,
3420
+ "encrypt": false,
3421
+ "isSystem": true
3422
+ },
3423
+ {
3424
+ "name": "templateFormat",
3425
+ "displayName": "Template Format",
3426
+ "type": "selectionStatic",
3427
+ "ormType": "varchar",
3428
+ "length": 25,
3429
+ "required": true,
3430
+ "index": false,
3431
+ "isSystem": true,
3432
+ "selectionValueType": "string",
3433
+ "selectionStaticValues": [
3434
+ "csv:csv",
3435
+ "excel:excel"
3436
+ ]
3437
+ },
3438
+ {
3439
+ "name": "notifyOnEmail",
3440
+ "displayName": "Notify On Email",
3441
+ "type": "boolean",
3442
+ "ormType": "boolean",
3443
+ "defaultValue": "false",
3444
+ "required": false,
3445
+ "unique": false,
3446
+ "index": false,
3447
+ "private": false,
3448
+ "encrypt": false,
3449
+ "isSystem": true
3450
+ },
3451
+ {
3452
+ "name": "modelMetadata",
3453
+ "displayName": "Related Model Metadata",
3454
+ "type": "relation",
3455
+ "ormType": "int",
3456
+ "required": true,
3457
+ "unique": false,
3458
+ "index": true,
3459
+ "private": false,
3460
+ "encrypt": false,
3461
+ "relationType": "many-to-one",
3462
+ "relationCoModelSingularName": "modelMetadata",
3463
+ "relationCreateInverse": false,
3464
+ "relationCascade": "cascade",
3465
+ "relationModelModuleName": "solid-core",
3466
+ "isSystem": true
3467
+ },
3468
+ {
3469
+ "name": "fields",
3470
+ "displayName": "Fields",
3471
+ "type": "json",
3472
+ "ormType": "varchar",
3473
+ "length": 2056,
3474
+ "required": true,
3475
+ "unique": false,
3476
+ "index": false,
3477
+ "private": false,
3478
+ "encrypt": false,
3479
+ "isSystem": true
3480
+ }
3481
+ ]
3482
+ },
3483
+ {
3484
+ "singularName": "exportTransaction",
3485
+ "tableName": "ss_export_transaction",
3486
+ "pluralName": "exportTransactions",
3487
+ "displayName": "Export Transactions",
3488
+ "description": "Model to capture all information related to a file export transaction",
3489
+ "dataSource": "default",
3490
+ "dataSourceType": "postgres",
3491
+ "isSystem": true,
3492
+ "userKeyFieldUserKey": "exportTransactionId",
3493
+ "fields": [
3494
+ {
3495
+ "name": "datetime",
3496
+ "displayName": "Transaction Date Time",
3497
+ "type": "datetime",
3498
+ "ormType": "timestamp",
3499
+ "required": true,
3500
+ "unique": false,
3501
+ "index": true,
3502
+ "private": false,
3503
+ "encrypt": false,
3504
+ "isSystem": true
3505
+ },
3506
+ {
3507
+ "name": "exportTransactionId",
3508
+ "displayName": "Transaction Id",
3509
+ "type":"computed",
3510
+ "ormType": "varchar",
3511
+ "length": 128,
3512
+ "required": true,
3513
+ "unique": true,
3514
+ "index": true,
3515
+ "private": false,
3516
+ "encrypt": false,
3517
+ "isSystem": true,
3518
+ "computedFieldValueType": "string",
3519
+ "computedFieldValueProvider": "UuidExternalIdComputedFieldProvider",
3520
+ "computedFieldValueProviderCtxt": "{\"prefix\": \"export_transaction\"}"
3521
+ },
3522
+ {
3523
+ "name": "status",
3524
+ "displayName": "Status",
3525
+ "type": "selectionStatic",
3526
+ "ormType": "varchar",
3527
+ "length": 25,
3528
+ "required": true,
3529
+ "index": true,
3530
+ "isSystem": true,
3531
+ "selectionValueType": "string",
3532
+ "selectionStaticValues": [
3533
+ "started:started",
3534
+ "failed:failed",
3535
+ "finished:finished"
3536
+ ]
3537
+ },
3538
+ {
3539
+ "name": "exportedFile",
3540
+ "displayName": "Exported File",
3541
+ "type": "mediaSingle",
3542
+ "required": false,
3543
+ "unique": true,
3544
+ "index": false,
3545
+ "private": false,
3546
+ "encrypt": false,
3547
+ "isSystem": true,
3548
+ "mediaStorageProviderUserKey": "default-filesystem"
3549
+ },
3550
+ {
3551
+ "name": "error",
3552
+ "displayName": "Error",
3553
+ "type": "longText",
3554
+ "ormType": "text",
3555
+ "required": false,
3556
+ "unique": false,
3557
+ "index": false,
3558
+ "private": false,
3559
+ "encrypt": false,
3560
+ "isSystem": true
3561
+ },
3562
+ {
3563
+ "name": "exportTemplate",
3564
+ "displayName": "Related Export Template",
3565
+ "type": "relation",
3566
+ "ormType": "int",
3567
+ "required": true,
3568
+ "unique": false,
3569
+ "index": true,
3570
+ "private": false,
3571
+ "encrypt": false,
3572
+ "relationType": "many-to-one",
3573
+ "relationCoModelSingularName": "exportTemplate",
3574
+ "relationCreateInverse": false,
3575
+ "relationCascade": "cascade",
3576
+ "relationModelModuleName": "solid-core",
3577
+ "isSystem": true
3578
+ }
3579
+ ]
3398
3580
  }
3399
3581
  ]
3400
3582
  },
@@ -0,0 +1,41 @@
1
+ import { PassThrough, Readable } from 'stream';
2
+ import { format } from 'fast-csv';
3
+ import { Injectable, Logger } from '@nestjs/common';
4
+
5
+ @Injectable()
6
+ export class CsvService {
7
+ private logger = new Logger(CsvService.name);
8
+ public async createCsvStream(
9
+ getDataRecords: (chunkIndex: number, chunkSize: number) => Promise<any[]>,
10
+ chunkSize: number
11
+ ): Promise<Readable> {
12
+ const passThrough = new PassThrough(); // ✅ Create a streaming pipe
13
+ const csvStream = format({ headers: true });
14
+
15
+ csvStream.pipe(passThrough); // ✅ Pipe CSV output to PassThrough stream
16
+
17
+ let chunkIndex = 0;
18
+
19
+ try {
20
+ while (true) {
21
+ const records = await getDataRecords(chunkIndex, chunkSize); // ✅ Fetch chunked data
22
+ if (records.length === 0) break; // ✅ Stop if no more records
23
+
24
+ for (const record of records) {
25
+ csvStream.write(record); // ✅ Write each record to the CSV stream
26
+ }
27
+
28
+ chunkIndex++; // ✅ Fetch next chunk
29
+ this.logger.debug(`✅ Chunk ${chunkIndex} written to CSV`);
30
+ }
31
+
32
+ csvStream.end(); // ✅ Ensure CSV stream is finalized
33
+ } catch (error) {
34
+ this.logger.error(`❌ Error writing CSV: ${error.message}`);
35
+ passThrough.destroy(error); // ✅ Properly destroy stream on error
36
+ throw error;
37
+ }
38
+
39
+ return passThrough; // ✅ Return the streaming response
40
+ }
41
+ }