@solidstarters/solid-core 1.2.73 → 1.2.76

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/module-metadata-seeder.service.js +1 -1
  41. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  42. package/dist/seeders/seed-data/solid-core-metadata.json +183 -1
  43. package/dist/services/csv.service.d.ts +6 -0
  44. package/dist/services/csv.service.d.ts.map +1 -0
  45. package/dist/services/csv.service.js +48 -0
  46. package/dist/services/csv.service.js.map +1 -0
  47. package/dist/services/excel.service.d.ts +6 -0
  48. package/dist/services/excel.service.d.ts.map +1 -0
  49. package/dist/services/excel.service.js +90 -0
  50. package/dist/services/excel.service.js.map +1 -0
  51. package/dist/services/export-template.service.d.ts +27 -0
  52. package/dist/services/export-template.service.d.ts.map +1 -0
  53. package/dist/services/export-template.service.js +79 -0
  54. package/dist/services/export-template.service.js.map +1 -0
  55. package/dist/services/export-transaction.service.d.ts +50 -0
  56. package/dist/services/export-transaction.service.d.ts.map +1 -0
  57. package/dist/services/export-transaction.service.js +191 -0
  58. package/dist/services/export-transaction.service.js.map +1 -0
  59. package/dist/services/file.service.d.ts +3 -0
  60. package/dist/services/file.service.d.ts.map +1 -1
  61. package/dist/services/file.service.js +18 -2
  62. package/dist/services/file.service.js.map +1 -1
  63. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts +4 -2
  64. package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
  65. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +4 -1
  66. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  67. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts +5 -3
  68. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
  69. package/dist/services/mediaStorageProviders/file-storage-provider.js +23 -3
  70. package/dist/services/mediaStorageProviders/file-storage-provider.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/module-metadata-seeder.service.ts +1 -1
  89. package/src/seeders/seed-data/solid-core-metadata.json +183 -1
  90. package/src/services/csv.service.ts +41 -0
  91. package/src/services/excel.service.ts +105 -0
  92. package/src/services/export-template.service.ts +71 -0
  93. package/src/services/export-transaction.service.ts +208 -0
  94. package/src/services/export_issues.txt +6 -0
  95. package/src/services/file.service.ts +26 -1
  96. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +9 -6
  97. package/src/services/mediaStorageProviders/file-storage-provider.ts +29 -7
  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.73",
3
+ "version": "1.2.76",
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
 
@@ -72,7 +72,7 @@ export class ModuleMetadataSeederService {
72
72
 
73
73
  async seed() {
74
74
 
75
- const typedSolidCoreMetadata: any = solidCoreMetadata;
75
+ const typedSolidCoreMetadata: any = structuredClone(solidCoreMetadata);
76
76
 
77
77
  // Run the permissions seeder.
78
78
  // await this.permissionsSeederService.seed();
@@ -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
+ }