@solidstarters/solid-core 1.2.88 → 1.2.90

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 (102) hide show
  1. package/dist/controllers/export-template.controller.d.ts +3 -2
  2. package/dist/controllers/export-template.controller.d.ts.map +1 -1
  3. package/dist/controllers/export-template.controller.js +23 -10
  4. package/dist/controllers/export-template.controller.js.map +1 -1
  5. package/dist/controllers/import-transaction-error-log.controller.d.ts +41 -0
  6. package/dist/controllers/import-transaction-error-log.controller.d.ts.map +1 -0
  7. package/dist/controllers/import-transaction-error-log.controller.js +179 -0
  8. package/dist/controllers/import-transaction-error-log.controller.js.map +1 -0
  9. package/dist/controllers/import-transaction.controller.d.ts +46 -0
  10. package/dist/controllers/import-transaction.controller.d.ts.map +1 -0
  11. package/dist/controllers/import-transaction.controller.js +236 -0
  12. package/dist/controllers/import-transaction.controller.js.map +1 -0
  13. package/dist/dtos/create-import-transaction-error-log.dto.d.ts +10 -0
  14. package/dist/dtos/create-import-transaction-error-log.dto.d.ts.map +1 -0
  15. package/dist/dtos/create-import-transaction-error-log.dto.js +65 -0
  16. package/dist/dtos/create-import-transaction-error-log.dto.js.map +1 -0
  17. package/dist/dtos/create-import-transaction.dto.d.ts +14 -0
  18. package/dist/dtos/create-import-transaction.dto.d.ts.map +1 -0
  19. package/dist/dtos/create-import-transaction.dto.js +90 -0
  20. package/dist/dtos/create-import-transaction.dto.js.map +1 -0
  21. package/dist/dtos/export.dto.d.ts +5 -0
  22. package/dist/dtos/export.dto.d.ts.map +1 -0
  23. package/dist/dtos/export.dto.js +12 -0
  24. package/dist/dtos/export.dto.js.map +1 -0
  25. package/dist/dtos/import-instructions.dto.d.ts +19 -0
  26. package/dist/dtos/import-instructions.dto.d.ts.map +1 -0
  27. package/dist/dtos/import-instructions.dto.js +110 -0
  28. package/dist/dtos/import-instructions.dto.js.map +1 -0
  29. package/dist/dtos/update-import-transaction-error-log.dto.d.ts +11 -0
  30. package/dist/dtos/update-import-transaction-error-log.dto.d.ts.map +1 -0
  31. package/dist/dtos/update-import-transaction-error-log.dto.js +72 -0
  32. package/dist/dtos/update-import-transaction-error-log.dto.js.map +1 -0
  33. package/dist/dtos/update-import-transaction.dto.d.ts +15 -0
  34. package/dist/dtos/update-import-transaction.dto.d.ts.map +1 -0
  35. package/dist/dtos/update-import-transaction.dto.js +91 -0
  36. package/dist/dtos/update-import-transaction.dto.js.map +1 -0
  37. package/dist/entities/export-transaction.entity.js +1 -1
  38. package/dist/entities/export-transaction.entity.js.map +1 -1
  39. package/dist/entities/import-transaction-error-log.entity.d.ts +11 -0
  40. package/dist/entities/import-transaction-error-log.entity.d.ts.map +1 -0
  41. package/dist/entities/import-transaction-error-log.entity.js +53 -0
  42. package/dist/entities/import-transaction-error-log.entity.js.map +1 -0
  43. package/dist/entities/import-transaction.entity.d.ts +11 -0
  44. package/dist/entities/import-transaction.entity.d.ts.map +1 -0
  45. package/dist/entities/import-transaction.entity.js +55 -0
  46. package/dist/entities/import-transaction.entity.js.map +1 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +2 -0
  50. package/dist/index.js.map +1 -1
  51. package/dist/seeders/seed-data/solid-core-metadata.json +317 -1
  52. package/dist/services/csv.service.d.ts +11 -1
  53. package/dist/services/csv.service.d.ts.map +1 -1
  54. package/dist/services/csv.service.js +72 -5
  55. package/dist/services/csv.service.js.map +1 -1
  56. package/dist/services/excel.service.d.ts +11 -1
  57. package/dist/services/excel.service.d.ts.map +1 -1
  58. package/dist/services/excel.service.js +68 -10
  59. package/dist/services/excel.service.js.map +1 -1
  60. package/dist/services/export-template.service.d.ts +3 -2
  61. package/dist/services/export-template.service.d.ts.map +1 -1
  62. package/dist/services/export-template.service.js +6 -6
  63. package/dist/services/export-template.service.js.map +1 -1
  64. package/dist/services/export-transaction.service.d.ts +6 -3
  65. package/dist/services/export-transaction.service.d.ts.map +1 -1
  66. package/dist/services/export-transaction.service.js +101 -22
  67. package/dist/services/export-transaction.service.js.map +1 -1
  68. package/dist/services/import-transaction-error-log.service.d.ts +22 -0
  69. package/dist/services/import-transaction-error-log.service.d.ts.map +1 -0
  70. package/dist/services/import-transaction-error-log.service.js +56 -0
  71. package/dist/services/import-transaction-error-log.service.js.map +1 -0
  72. package/dist/services/import-transaction.service.d.ts +64 -0
  73. package/dist/services/import-transaction.service.d.ts.map +1 -0
  74. package/dist/services/import-transaction.service.js +231 -0
  75. package/dist/services/import-transaction.service.js.map +1 -0
  76. package/dist/solid-core.module.d.ts.map +1 -1
  77. package/dist/solid-core.module.js +13 -1
  78. package/dist/solid-core.module.js.map +1 -1
  79. package/dist/tsconfig.tsbuildinfo +1 -1
  80. package/package.json +1 -1
  81. package/src/controllers/export-template.controller.ts +20 -7
  82. package/src/controllers/import-transaction-error-log.controller.ts +93 -0
  83. package/src/controllers/import-transaction.controller.ts +128 -0
  84. package/src/dtos/create-import-transaction-error-log.dto.ts +34 -0
  85. package/src/dtos/create-import-transaction.dto.ts +50 -0
  86. package/src/dtos/export.dto.ts +5 -0
  87. package/src/dtos/import-instructions.dto.ts +66 -0
  88. package/src/dtos/update-import-transaction-error-log.dto.ts +39 -0
  89. package/src/dtos/update-import-transaction.dto.ts +52 -0
  90. package/src/entities/export-transaction.entity.ts +1 -1
  91. package/src/entities/import-transaction-error-log.entity.ts +22 -0
  92. package/src/entities/import-transaction.entity.ts +22 -0
  93. package/src/index.ts +2 -0
  94. package/src/seeders/seed-data/solid-core-metadata.json +319 -3
  95. package/src/services/csv.service.ts +116 -7
  96. package/src/services/excel.service.ts +109 -64
  97. package/src/services/export-template.service.ts +7 -6
  98. package/src/services/export-transaction.service.ts +136 -25
  99. package/src/services/export_issues.txt +5 -1
  100. package/src/services/import-transaction-error-log.service.ts +34 -0
  101. package/src/services/import-transaction.service.ts +281 -0
  102. package/src/solid-core.module.ts +13 -1
@@ -0,0 +1,281 @@
1
+ import { Injectable, Logger } from '@nestjs/common';
2
+ import { DiscoveryService, ModuleRef } from "@nestjs/core";
3
+ import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
4
+ import { EntityManager, Repository } from 'typeorm';
5
+
6
+ import { ConfigService } from '@nestjs/config';
7
+ import { CrudHelperService } from 'src/services/crud-helper.service';
8
+ import { CRUDService } from 'src/services/crud.service';
9
+ import { FileService } from 'src/services/file.service';
10
+ import { ModelMetadataService } from 'src/services/model-metadata.service';
11
+ import { ModuleMetadataService } from 'src/services/module-metadata.service';
12
+
13
+
14
+ import { HttpService } from '@nestjs/axios';
15
+ import { SolidFieldType } from 'src/dtos/create-field-metadata.dto';
16
+ import { ImportInstructionsResponseDto, StandardImportInstructionsResponseDto } from 'src/dtos/import-instructions.dto';
17
+ import { FieldMetadata } from 'src/entities/field-metadata.entity';
18
+ import { MediaWithFullUrl } from 'src/interfaces';
19
+ import { Readable } from 'stream';
20
+ import { ImportTransaction } from '../entities/import-transaction.entity';
21
+ import { CsvService } from './csv.service';
22
+ import { ExcelService } from './excel.service';
23
+
24
+ interface ImportTemplateFileInfo {
25
+ stream: NodeJS.ReadableStream;
26
+ fileName: string;
27
+ mimeType: string;
28
+ }
29
+
30
+ export enum ImportFormat {
31
+ CSV = 'csv',
32
+ EXCEL = 'excel',
33
+ }
34
+ export interface ImportMappingInfo {
35
+ sampleImportedRecordInfo: SampleImportedRecordInfo[];
36
+ importableFields: ImportableFieldInfo[];
37
+ }
38
+ export interface SampleImportedRecordInfo {
39
+ cellHeader: string; // The header of the cell in the import file
40
+ cellValue: string; // The value of the cell in the import file
41
+ defaultMappedFieldName: string; // The default mapped field name in the model metadata
42
+ }
43
+ export interface ImportableFieldInfo {
44
+ name: string;
45
+ displayName: string;
46
+ }
47
+
48
+ export interface ImportReadResult {
49
+ headers: string[]; // Headers of the CSV file
50
+ data: Record<string, any>[]; // Data records in the current page
51
+ }
52
+
53
+ @Injectable()
54
+ export class ImportTransactionService extends CRUDService<ImportTransaction> {
55
+ constructor(
56
+ readonly modelMetadataService: ModelMetadataService,
57
+ readonly moduleMetadataService: ModuleMetadataService,
58
+ readonly configService: ConfigService,
59
+ readonly fileService: FileService,
60
+ readonly discoveryService: DiscoveryService,
61
+ readonly crudHelperService: CrudHelperService,
62
+ @InjectEntityManager()
63
+ readonly entityManager: EntityManager,
64
+ @InjectRepository(ImportTransaction, 'default')
65
+ readonly repo: Repository<ImportTransaction>,
66
+ readonly moduleRef: ModuleRef,
67
+ readonly excelService: ExcelService,
68
+ readonly csvService: CsvService,
69
+ readonly httpService: HttpService
70
+
71
+ ) {
72
+ super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'importTransaction', 'solid-core', moduleRef);
73
+ }
74
+
75
+ private readonly logger = new Logger(ImportTransactionService.name);
76
+ saveImportMapping(arg0: number) {
77
+ throw new Error('Method not implemented.');
78
+ }
79
+
80
+ async getImportMappingInfo(importTransactionId: number): Promise<ImportMappingInfo> {
81
+ // Load the import transaction for the given ID
82
+ const importTransaction = await this.findOne(importTransactionId, {
83
+ populate: ['modelMetadata', 'modelMetadata.fields'],
84
+ populateMedia: ['fileLocation'],
85
+ });
86
+ if (!importTransaction) {
87
+ throw new Error(`Import transaction with ID ${importTransactionId} not found.`);
88
+ }
89
+
90
+ // Get all the importable fields from the model metadata
91
+ const importableFields: ImportableFieldInfo[] = this.fieldsAllowedForImport(importTransaction.modelMetadata.fields).map(field => ({
92
+ name: field.name,
93
+ displayName: field.displayName,
94
+ }));
95
+
96
+ // Get the import file stream for the import transaction
97
+ const importFileMediaObject = importTransaction['_media']['fileLocation'][0] as MediaWithFullUrl; // Since there can be only one fileLocation, we can safely access the first element
98
+ if (!importFileMediaObject) {
99
+ throw new Error(`Import file for transaction ID ${importTransactionId} not found.`);
100
+ }
101
+ const importFileStream = await this.getImportFileStream(importFileMediaObject);
102
+
103
+ // Get a sample of records from the import file
104
+ const sampleRecord = await this.getFileRecordsSample(importFileStream, importFileMediaObject.mimeType);
105
+
106
+ // Convert sampleRecord to the format required for SampleImportedRecordInfo
107
+ const wrappedRecords: SampleImportedRecordInfo[] = sampleRecord.data.map((record: Record<string, any>) => {
108
+ return Object.entries(record).map(([key, value]) => ({
109
+ cellHeader: key,
110
+ cellValue: value,
111
+ defaultMappedFieldName: importableFields.find(field => field.displayName === key)?.name || '',
112
+ }));
113
+ }).flat();
114
+
115
+ // for await (const page of this.csvService.readCsvInPagesFromStream(importFileStream)) {
116
+ // // await dbService.bulkInsert(page);
117
+ // }
118
+
119
+ return {
120
+ sampleImportedRecordInfo: wrappedRecords, // This will hold the sample data from the file
121
+ importableFields: importableFields, // This will hold the fields that can be imported
122
+ };
123
+ }
124
+
125
+ private async getFileRecordsSample(importFileStream: Readable, mimeType: string): Promise<ImportReadResult> {
126
+ // Depending upon the mime type of the file, read the file in pages
127
+ // For CSV files, use the csvService to read the file in pages
128
+ // For Excel files, use the excelService to read the file in pages
129
+ if (mimeType === 'text/csv') {
130
+ const generator = this.csvService.readCsvInPagesFromStream(importFileStream, { pageSize: 1 });
131
+ const firstRecord = await generator.next(); // Get the first record to extract headers and sample data
132
+ return firstRecord.value;
133
+ } else if (mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
134
+ const generator = this.excelService.readExcelInPagesFromStream(importFileStream, { pageSize: 1 });
135
+ const firstRecord = await generator.next(); // Get the first record to extract headers and sample data
136
+ return firstRecord.value;
137
+ }
138
+ else { // If the file is neither CSV nor Excel, throw an error
139
+ throw new Error(`Unsupported file type: ${mimeType}`);
140
+ }
141
+ }
142
+
143
+ private async getImportFileStream(importFileMediaObject: MediaWithFullUrl): Promise<Readable> {
144
+ const fileUrl = importFileMediaObject['_full_url'];
145
+ const mimeType = importFileMediaObject['mimeType'];
146
+ if (!fileUrl) {
147
+ throw new Error(`File URL ${fileUrl} not found.`);
148
+ }
149
+ // From the file URL, convert the file URL to a readable stream using nestjs http service and axios
150
+ const fileUrlResponse = await this.httpService.axiosRef.get(fileUrl, {
151
+ responseType: 'stream',
152
+ });
153
+
154
+ if (!fileUrlResponse || !fileUrlResponse.data) {
155
+ throw new Error(`Failed to read file from URL: ${fileUrl}`);
156
+ }
157
+ // fileUrlResponse.data is a Node.js Readable stream
158
+ return fileUrlResponse.data;
159
+ }
160
+
161
+ async getImportInstructions(modelMetadataId: number): Promise<ImportInstructionsResponseDto> {
162
+ // Load the model metadata for the given ID
163
+ const modelMetadata = await this.modelMetadataService.findOne(modelMetadataId, {
164
+ populate: ['fields'],
165
+ });
166
+ if (!modelMetadata) {
167
+ throw new Error(`Model metadata with ID ${modelMetadataId} not found.`);
168
+ }
169
+
170
+ // Create the standard import instructions
171
+ const standardInstructions: StandardImportInstructionsResponseDto = {
172
+ requiredFields: [],
173
+ dateFields: [],
174
+ dateTimeFields: [],
175
+ numberFields: [],
176
+ emailFields: [],
177
+ regexFields: [],
178
+ jsonFields: [],
179
+ booleanFields: [],
180
+ };
181
+
182
+ // Iterate through the fields and populate the standard instructions
183
+ for (const field of modelMetadata.fields) {
184
+ if (field.isSystem) continue; // Skip system fields
185
+ if (field.required) {
186
+ standardInstructions.requiredFields.push(field.displayName);
187
+ }
188
+ if (field.type === SolidFieldType.date) {
189
+ standardInstructions.dateFields.push(field.displayName);
190
+ }
191
+ if (field.type === SolidFieldType.datetime) {
192
+ standardInstructions.dateTimeFields.push(field.displayName);
193
+ }
194
+ if ([SolidFieldType.bigint, SolidFieldType.int, SolidFieldType.decimal].includes(field.type as SolidFieldType)) {
195
+ standardInstructions.numberFields.push(field.displayName);
196
+ }
197
+ if (field.type === SolidFieldType.email) {
198
+ standardInstructions.emailFields.push(field.displayName);
199
+ }
200
+ if (field.regexPattern) {
201
+ standardInstructions.regexFields.push({
202
+ fieldName: field.displayName,
203
+ regexPattern: field.regexPattern,
204
+ });
205
+ }
206
+ if (field.type === SolidFieldType.json) {
207
+ standardInstructions.jsonFields.push(field.displayName);
208
+ }
209
+ if (field.type === SolidFieldType.boolean) {
210
+ standardInstructions.booleanFields.push(field.displayName);
211
+ }
212
+ }
213
+
214
+ // Create the custom instructions
215
+ const customInstructions: string[] = [];
216
+
217
+ return {
218
+ standard: standardInstructions,
219
+ custom: customInstructions,
220
+ };
221
+ }
222
+
223
+ /**
224
+ * This method is used to return a csv / excel template for the import transaction
225
+ * It will contain the display names of the fields in the header row
226
+ * @param modelMetadataId
227
+ */
228
+ async getImportTemplate(modelMetadataId: number, format: ImportFormat = ImportFormat.CSV): Promise<ImportTemplateFileInfo> {
229
+ // Load the model metadata for the given ID
230
+ const modelMetadata = await this.modelMetadataService.findOne(modelMetadataId, {
231
+ populate: ['fields'],
232
+ });
233
+ if (!modelMetadata) {
234
+ throw new Error(`Model metadata with ID ${modelMetadataId} not found.`);
235
+ }
236
+ // Create a header row with the display names of the fields, excluding the media fields,computed fields
237
+ const headers = this.fieldsAllowedForImport(modelMetadata.fields)
238
+ .map(field => field.displayName);
239
+
240
+ // Depending on the format, generate the template
241
+ if (format === ImportFormat.CSV) {
242
+ const stream = await this.csvService.createCsvStream(null, 0, headers); // Create a CSV stream with the header row
243
+ const fileName = `${modelMetadata.singularName}-import-template.csv`;
244
+ const mimeType = 'text/csv';
245
+ return {
246
+ stream,
247
+ fileName,
248
+ mimeType,
249
+ };
250
+ } else if (format === ImportFormat.EXCEL) {
251
+ const stream = await this.excelService.createExcelStream(null, 0, headers); // Create an Excel stream with the header row
252
+ const fileName = `${modelMetadata.singularName}-import-template.xlsx`;
253
+ // Set the MIME type for Excel files
254
+ const mimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
255
+ return {
256
+ stream,
257
+ fileName,
258
+ mimeType,
259
+ };
260
+ } else {
261
+ throw new Error(`Unsupported import format: ${format}`);
262
+ }
263
+
264
+ }
265
+
266
+ private fieldsAllowedForImport(fields: FieldMetadata[]): FieldMetadata[] {
267
+ // Filter out fields that are not allowed for import
268
+ return fields.filter(field =>
269
+ field.type !== SolidFieldType.mediaMultiple && // Exclude media multiple fields
270
+ field.type !== SolidFieldType.mediaSingle &&
271
+ field.type !== SolidFieldType.computed && // Exclude computed fields
272
+ field.type !== SolidFieldType.password &&
273
+ field.type !== SolidFieldType.richText &&
274
+ field.type !== SolidFieldType.uuid &&
275
+ field.isSystem !== true // Exclude system fields
276
+ );
277
+ }
278
+
279
+
280
+
281
+ }
@@ -171,6 +171,12 @@ import { ExcelService } from './services/excel.service';
171
171
  import { CsvService } from './services/csv.service';
172
172
  import { ClsModule } from 'nestjs-cls';
173
173
  import { FieldRepository } from './repository/field.repository';
174
+ import { ImportTransaction } from './entities/import-transaction.entity';
175
+ import { ImportTransactionService } from './services/import-transaction.service';
176
+ import { ImportTransactionController } from './controllers/import-transaction.controller';
177
+ import { ImportTransactionErrorLog } from './entities/import-transaction-error-log.entity';
178
+ import { ImportTransactionErrorLogService } from './services/import-transaction-error-log.service';
179
+ import { ImportTransactionErrorLogController } from './controllers/import-transaction-error-log.controller';
174
180
 
175
181
 
176
182
  @Global()
@@ -229,6 +235,8 @@ import { FieldRepository } from './repository/field.repository';
229
235
  middleware: {
230
236
  mount: true,
231
237
  }}),
238
+ TypeOrmModule.forFeature([ImportTransaction]),
239
+ TypeOrmModule.forFeature([ImportTransactionErrorLog]),
232
240
  ],
233
241
  controllers: [
234
242
  ModuleMetadataController,
@@ -262,6 +270,8 @@ import { FieldRepository } from './repository/field.repository';
262
270
  ChatterMessageDetailsController,
263
271
  ExportTemplateController,
264
272
  ExportTransactionController,
273
+ ImportTransactionController,
274
+ ImportTransactionErrorLogController,
265
275
  ],
266
276
  providers: [
267
277
  {
@@ -370,7 +380,9 @@ import { FieldRepository } from './repository/field.repository';
370
380
  ExportTransactionService,
371
381
  ExcelService,
372
382
  CsvService,
373
- FieldRepository
383
+ FieldRepository,
384
+ ImportTransactionService,
385
+ ImportTransactionErrorLogService,
374
386
  ],
375
387
  exports: [
376
388
  ModuleMetadataService,