@solidstarters/solid-core 1.2.177 → 1.2.179
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controllers/ai-interaction.controller.js.map +1 -1
- package/dist/controllers/authentication.controller.d.ts.map +1 -1
- package/dist/controllers/authentication.controller.js +1 -0
- package/dist/controllers/authentication.controller.js.map +1 -1
- package/dist/controllers/setting.controller.d.ts +2 -2
- package/dist/controllers/setting.controller.d.ts.map +1 -1
- package/dist/controllers/setting.controller.js +5 -3
- package/dist/controllers/setting.controller.js.map +1 -1
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js +5 -3
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +13 -1
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +2 -1
- package/dist/services/ai-interaction.service.d.ts.map +1 -1
- package/dist/services/ai-interaction.service.js +1 -0
- package/dist/services/ai-interaction.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js +24 -5
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/genai/ingest-metadata.service.d.ts.map +1 -1
- package/dist/services/genai/ingest-metadata.service.js +9 -8
- package/dist/services/genai/ingest-metadata.service.js.map +1 -1
- package/dist/services/import-transaction.service.d.ts.map +1 -1
- package/dist/services/import-transaction.service.js +32 -8
- package/dist/services/import-transaction.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/controllers/ai-interaction.controller.ts +1 -1
- package/src/controllers/authentication.controller.ts +1 -0
- package/src/controllers/setting.controller.ts +6 -5
- package/src/jobs/database/trigger-mcp-client-subscriber-database.service.ts +9 -4
- package/src/seeders/module-metadata-seeder.service.ts +29 -3
- package/src/seeders/seed-data/solid-core-metadata.json +3 -1
- package/src/services/ai-interaction.service.ts +1 -0
- package/src/services/chatter-message.service.ts +26 -4
- package/src/services/genai/ingest-metadata.service.ts +34 -8
- package/src/services/import-transaction.service.ts +36 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidstarters/solid-core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.179",
|
|
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",
|
|
@@ -95,7 +95,7 @@ export class AiInteractionController {
|
|
|
95
95
|
@ApiBearerAuth("jwt")
|
|
96
96
|
@Post('/trigger-mcp-client-job')
|
|
97
97
|
async triggerMcpClientJob(@Body() dto: InvokeAiPromptDto, @ActiveUser() activeUser: ActiveUserData) {
|
|
98
|
-
return this.service.triggerMcpClientJob(dto,activeUser.sub);
|
|
98
|
+
return this.service.triggerMcpClientJob(dto, activeUser.sub);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
@ApiBearerAuth("jwt")
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BadRequestException, Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, UploadedFiles, UseInterceptors } from '@nestjs/common';
|
|
2
2
|
import { AnyFilesInterceptor } from "@nestjs/platform-express";
|
|
3
3
|
import { ApiBearerAuth, ApiQuery, ApiTags } from '@nestjs/swagger';
|
|
4
|
-
import {
|
|
5
|
-
import { CreateSettingDto } from '../dtos/create-setting.dto';
|
|
6
|
-
import { UpdateSettingDto } from '../dtos/update-setting.dto';
|
|
4
|
+
import { Public } from 'src/decorators/public.decorator';
|
|
7
5
|
import { SolidRequestContextDecorator } from 'src/decorators/solid-request-context.decorator';
|
|
8
6
|
import { SolidRequestContextDto } from 'src/dtos/solid-request-context.dto';
|
|
9
|
-
import {
|
|
7
|
+
import { CreateSettingDto } from '../dtos/create-setting.dto';
|
|
8
|
+
import { UpdateSettingDto } from '../dtos/update-setting.dto';
|
|
9
|
+
import { SettingService } from '../services/setting.service';
|
|
10
10
|
|
|
11
11
|
@ApiTags('Solid Core')
|
|
12
12
|
@Controller('setting') //FIXME: Change this to the model plural name
|
|
@@ -44,6 +44,7 @@ export class SettingController {
|
|
|
44
44
|
|
|
45
45
|
@ApiBearerAuth("jwt")
|
|
46
46
|
@Get('/wrapped')
|
|
47
|
+
@Public()
|
|
47
48
|
async wrapSettings() {
|
|
48
49
|
return this.service.wrapSettings();
|
|
49
50
|
}
|
|
@@ -92,7 +92,7 @@ export class TriggerMcpClientSubscriberDatabase extends DatabaseSubscriber<Trigg
|
|
|
92
92
|
responseTimeMs: 0, // Updated after we receive the response
|
|
93
93
|
metadata: '', // Updated in the tool
|
|
94
94
|
isApplied: false, // Updated after we receive the response
|
|
95
|
-
status: '' // Updated after we receive the response
|
|
95
|
+
status: 'pending' // Updated after we receive the response
|
|
96
96
|
});
|
|
97
97
|
|
|
98
98
|
const finalPrompt = `
|
|
@@ -148,7 +148,7 @@ export class TriggerMcpClientSubscriberDatabase extends DatabaseSubscriber<Trigg
|
|
|
148
148
|
throw new Error(errorsStr);
|
|
149
149
|
}
|
|
150
150
|
else {
|
|
151
|
-
|
|
151
|
+
let nestedResponse = this.cleanNestedResponse(aiResponse);
|
|
152
152
|
|
|
153
153
|
// const genAiInteraction = await this.aiInteractionService.create({
|
|
154
154
|
// userId: aiInteraction.user.id,
|
|
@@ -166,14 +166,19 @@ export class TriggerMcpClientSubscriberDatabase extends DatabaseSubscriber<Trigg
|
|
|
166
166
|
// });
|
|
167
167
|
|
|
168
168
|
// TODO: Update the previously created genAiInteraction record with the respective success fields and save to DB
|
|
169
|
+
const errorsStr = nestedResponse.status == "error" && nestedResponse.errors.join('\n ');
|
|
170
|
+
|
|
169
171
|
await this.aiInteractionService.update(genAiInteraction.id, {
|
|
172
|
+
errorMessage: nestedResponse.status == "error" ? `${errorsStr}` : "",
|
|
173
|
+
// errorMessage:"",
|
|
170
174
|
// contentType: aiResponse.content_type,
|
|
171
|
-
errorMessage: '',
|
|
172
175
|
// message: nestedResponse,
|
|
173
176
|
modelUsed: aiResponse.model,
|
|
174
177
|
responseTimeMs: aiResponse.duration_ms,
|
|
175
178
|
isApplied: aiInteraction.isApplied,
|
|
176
|
-
|
|
179
|
+
|
|
180
|
+
// status: aiResponse.success ? 'succeeded' : 'failed'
|
|
181
|
+
status: aiResponse.success && nestedResponse.status == "success" ? 'succeeded' : 'failed'
|
|
177
182
|
}, [], true);
|
|
178
183
|
|
|
179
184
|
// If the human interaction was with isAutoApply=true, then we can go ahead and autoApply.
|
|
@@ -241,21 +241,27 @@ export class ModuleMetadataSeederService {
|
|
|
241
241
|
// 1. Give all permissions to the Admin role.
|
|
242
242
|
this.logger.log(`About to add all permissions to the Admin role`);
|
|
243
243
|
await this.roleService.addAllPermissionsToRole("Admin");
|
|
244
|
-
// 2. Give
|
|
244
|
+
// 2. Give permissions to the Internal / Public role.
|
|
245
245
|
const internalRolePermission = [
|
|
246
|
-
|
|
246
|
+
// User permissions
|
|
247
|
+
'UserController.findMany', //Why do we need this?
|
|
247
248
|
'UserController.checkIfPermissionExists',
|
|
248
249
|
'UserController.findOne',
|
|
250
|
+
// Menu permissions
|
|
249
251
|
'MenuItemMetadataController.findMany',
|
|
250
252
|
'MenuItemMetadataController.findUserMenus',
|
|
251
253
|
'MenuItemMetadataController.findOne',
|
|
254
|
+
// View metadata permissions
|
|
252
255
|
'ViewMetadataController.getLayout',
|
|
253
256
|
'ViewMetadataController.findMany',
|
|
254
257
|
'ViewMetadataController.findOne',
|
|
258
|
+
// IAM permissions
|
|
255
259
|
'AuthenticationController.changePassword',
|
|
260
|
+
// Field Metadata permissions
|
|
256
261
|
'FieldMetadataController.getSelectionDynamicValues',
|
|
257
262
|
'FieldMetadataController.getSelectionDynamicValue',
|
|
258
263
|
'FieldMetadataController.findFieldDefaultMetaData',
|
|
264
|
+
// Saved Filters permissions
|
|
259
265
|
'SavedFiltersController.delete',
|
|
260
266
|
'SavedFiltersController.deleteMany',
|
|
261
267
|
'SavedFiltersController.findOne',
|
|
@@ -266,7 +272,27 @@ export class ModuleMetadataSeederService {
|
|
|
266
272
|
'SavedFiltersController.update',
|
|
267
273
|
'SavedFiltersController.insertMany',
|
|
268
274
|
'SavedFiltersController.create',
|
|
269
|
-
|
|
275
|
+
// Logout permissions
|
|
276
|
+
'AuthenticationController.logout',
|
|
277
|
+
// Other permissions can be added here as required.
|
|
278
|
+
// Chatter permissions
|
|
279
|
+
'ChatterMessageController.create',
|
|
280
|
+
'ChatterMessageController.getChatterMessages',
|
|
281
|
+
// 'ChatterMessageController.postMessage', // Does not seem to be used from ui
|
|
282
|
+
// Import
|
|
283
|
+
'ImportTransactionController.getImportTemplate',
|
|
284
|
+
'ImportTransactionController.getImportInstructions',
|
|
285
|
+
'ImportTransactionController.getImportMappingInfo',
|
|
286
|
+
'ImportTransactionController.startImportSync',
|
|
287
|
+
'ImportTransactionController.startImportAsync',
|
|
288
|
+
'ImportTransactionController.exportFailedImportedImports',
|
|
289
|
+
// Export permissions
|
|
290
|
+
'ExportTemplateController.startExportSync',
|
|
291
|
+
'ExportTemplateController.startExportAsync',
|
|
292
|
+
// List of values
|
|
293
|
+
'ListOfValuesController.findMany',
|
|
294
|
+
'ListOfValuesController.findOne',
|
|
295
|
+
// Media // [Not required], since media is always populated as part of a model
|
|
270
296
|
]
|
|
271
297
|
await this.roleService.addPermissionToRole('Internal User', internalRolePermission);
|
|
272
298
|
await this.roleService.addPermissionToRole('Public', ['SettingController.wrapSettings', 'AuthenticationController.logout']);
|
|
@@ -12331,7 +12331,9 @@
|
|
|
12331
12331
|
"type": "field",
|
|
12332
12332
|
"attrs": {
|
|
12333
12333
|
"name": "metadata",
|
|
12334
|
-
"height": "80vh"
|
|
12334
|
+
"height": "80vh",
|
|
12335
|
+
"viewWidget":"SolidAiInteractionMetadataFieldFormWidget"
|
|
12336
|
+
|
|
12335
12337
|
}
|
|
12336
12338
|
}
|
|
12337
12339
|
]
|
|
@@ -149,6 +149,7 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
|
|
|
149
149
|
}
|
|
150
150
|
catch (ex) {
|
|
151
151
|
this.logger.warn(`Attempting to parse mcp client response assuming it is JSON, however it is not: ${parsedResponse}`);
|
|
152
|
+
raw.success = false
|
|
152
153
|
}
|
|
153
154
|
// Parse the response string into an object
|
|
154
155
|
// const parsedResponse = JSON.parse(raw.response);
|
|
@@ -148,7 +148,7 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
148
148
|
messageDetail.oldValue = null;
|
|
149
149
|
messageDetail.oldValueDisplay = null;
|
|
150
150
|
messageDetail.newValue = this.formatFieldValue(field, fieldValue);
|
|
151
|
-
messageDetail.newValueDisplay = this.formatFieldValueDisplay(field, fieldValue);
|
|
151
|
+
messageDetail.newValueDisplay = await this.formatFieldValueDisplay(field, fieldValue);
|
|
152
152
|
await this.chatterMessageDetailsRepo.save(messageDetail);
|
|
153
153
|
}
|
|
154
154
|
}
|
|
@@ -263,8 +263,8 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
263
263
|
messageDetail.fieldDisplayName = field.displayName;
|
|
264
264
|
messageDetail.oldValue = this.formatFieldValue(field, oldValue);
|
|
265
265
|
messageDetail.newValue = this.formatFieldValue(field, newValue);
|
|
266
|
-
messageDetail.oldValueDisplay = this.formatFieldValueDisplay(field, oldValue);
|
|
267
|
-
messageDetail.newValueDisplay = this.formatFieldValueDisplay(field, newValue);
|
|
266
|
+
messageDetail.oldValueDisplay = await this.formatFieldValueDisplay(field, oldValue);
|
|
267
|
+
messageDetail.newValueDisplay = await this.formatFieldValueDisplay(field, newValue);
|
|
268
268
|
await this.chatterMessageDetailsRepo.save(messageDetail);
|
|
269
269
|
}
|
|
270
270
|
}
|
|
@@ -328,7 +328,7 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
328
328
|
return value.toString();
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
private formatFieldValueDisplay(field: any, value: any): string {
|
|
331
|
+
private async formatFieldValueDisplay(field: any, value: any): Promise<string> {
|
|
332
332
|
if (value === null || value === undefined) {
|
|
333
333
|
return '';
|
|
334
334
|
}
|
|
@@ -339,8 +339,30 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
339
339
|
|
|
340
340
|
if (field.type === 'relation') {
|
|
341
341
|
if (field.relationType === "many-to-one") {
|
|
342
|
+
if (value.name) {
|
|
342
343
|
return value.name;
|
|
343
344
|
}
|
|
345
|
+
|
|
346
|
+
try {
|
|
347
|
+
const relatedModel = await this.modelMetadataRepo.findOne({
|
|
348
|
+
where: { singularName: field.relationCoModelSingularName || field.relation },
|
|
349
|
+
relations: { userKeyField: true }
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
if (relatedModel && relatedModel.userKeyField) {
|
|
353
|
+
const userKeyFieldName = relatedModel.userKeyField.name;
|
|
354
|
+
return value[userKeyFieldName] ? value[userKeyFieldName].toString() : '';
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (value.id) {
|
|
358
|
+
return value.id.toString();
|
|
359
|
+
}
|
|
360
|
+
} catch (error) {
|
|
361
|
+
console.error('Error fetching related model metadata:', error);
|
|
362
|
+
return value.id ? value.id.toString() : '';
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
344
366
|
if (field.relationType === 'many-to-many') {
|
|
345
367
|
return value.map(item => item.name).join(', ');
|
|
346
368
|
}
|
|
@@ -121,7 +121,7 @@ export class IngestMetadataService {
|
|
|
121
121
|
ingestionInfo.collectionId = collectionId;
|
|
122
122
|
|
|
123
123
|
// Delete and re-insert a document representing the full json...
|
|
124
|
-
await this.deleteInsertRagDocumentForModuleMetadataJsonFile(ingestionInfo, fullPath, fileName)
|
|
124
|
+
// await this.deleteInsertRagDocumentForModuleMetadataJsonFile(ingestionInfo, fullPath, fileName)
|
|
125
125
|
|
|
126
126
|
// Delete and re-insert a chunk representing the module.
|
|
127
127
|
await this.deleteInsertRagChunkForModule(ingestionInfo, moduleMetadata);
|
|
@@ -129,11 +129,29 @@ export class IngestMetadataService {
|
|
|
129
129
|
// Delete and re-insert chunks representing each model.
|
|
130
130
|
for (const model of moduleMetadata.models) {
|
|
131
131
|
await this.deleteInsertRagChunkForModel(ingestionInfo, enabledModule, model);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
|
|
133
|
+
// Disabling this for now...
|
|
134
|
+
// for (const field of model.fields) {
|
|
135
|
+
// await this.deleteInsertRagChunkForField(ingestionInfo, enabledModule, model.singularName, field);
|
|
136
|
+
// }
|
|
135
137
|
}
|
|
136
138
|
|
|
139
|
+
// TODO: Delete and re-insert chunks representing roles
|
|
140
|
+
|
|
141
|
+
// TODO: Delete and re-insert chunks representing menus
|
|
142
|
+
|
|
143
|
+
// TODO: Delete and re-insert chunks representing actions
|
|
144
|
+
|
|
145
|
+
// TODO: Delete and re-insert chunks representing list views
|
|
146
|
+
|
|
147
|
+
// TODO: Delete and re-insert chunks representing kanban views
|
|
148
|
+
|
|
149
|
+
// TODO: Delete and re-insert chunks representing form views
|
|
150
|
+
|
|
151
|
+
// TODO: Delete and re-insert chunks representing security rules
|
|
152
|
+
|
|
153
|
+
// TODO: Delete and re-insert chunks representing scheduled jobs
|
|
154
|
+
|
|
137
155
|
// Save ingestion info to disk...
|
|
138
156
|
fs.writeFileSync(enabledModulIngestionInfoFullPath, JSON.stringify({ ...ingestionInfo }, null, 2), 'utf8');
|
|
139
157
|
}
|
|
@@ -242,7 +260,7 @@ export class IngestMetadataService {
|
|
|
242
260
|
description: m?.description ?? null,
|
|
243
261
|
|
|
244
262
|
// Include field names to detect field-level changes at module granularity - maybe remove this later?
|
|
245
|
-
fields: Array.isArray(m?.fields) ? m.fields.map((f: any) => f?.name ?? null) : [],
|
|
263
|
+
// fields: Array.isArray(m?.fields) ? m.fields.map((f: any) => f?.name ?? null) : [],
|
|
246
264
|
})),
|
|
247
265
|
});
|
|
248
266
|
|
|
@@ -270,6 +288,7 @@ Usage: Use this chunk to choose the correct model/field chunks for code generati
|
|
|
270
288
|
|
|
271
289
|
// metadata has to be concise and queryable
|
|
272
290
|
const metadata = {
|
|
291
|
+
collectionName: `${moduleName}-rag-collection`,
|
|
273
292
|
kind: 'solidx-metadata',
|
|
274
293
|
type: 'module',
|
|
275
294
|
moduleName,
|
|
@@ -288,7 +307,8 @@ Usage: Use this chunk to choose the correct model/field chunks for code generati
|
|
|
288
307
|
}
|
|
289
308
|
|
|
290
309
|
const r = await this.ragClient.documents.create({
|
|
291
|
-
|
|
310
|
+
chunks: [text],
|
|
311
|
+
// raw_text: text,
|
|
292
312
|
metadata: metadata,
|
|
293
313
|
collectionIds: [ingestionInfo.collectionId],
|
|
294
314
|
});
|
|
@@ -364,10 +384,15 @@ Fields (${fields.length}) [name:type|flags]:
|
|
|
364
384
|
${fieldSummaryLines.join('\n')}
|
|
365
385
|
|
|
366
386
|
Usage: Use this chunk to generate DTOs, subscribers, custom service methods, and CRUD handlers for ${modelName}.
|
|
367
|
-
|
|
387
|
+
|
|
388
|
+
Full model metadata json:
|
|
389
|
+
${JSON.stringify(model)}
|
|
390
|
+
|
|
391
|
+
`;
|
|
368
392
|
|
|
369
393
|
// 4) Metadata (concise & queryable)
|
|
370
394
|
const metadata = {
|
|
395
|
+
collectionName: `${moduleName}-rag-collection`,
|
|
371
396
|
kind: 'solidx-metadata',
|
|
372
397
|
type: 'model',
|
|
373
398
|
moduleName,
|
|
@@ -392,7 +417,7 @@ For exact constraints (enum/min/max/regex/default), consult the individual field
|
|
|
392
417
|
|
|
393
418
|
// 6) Create new document (R2R auto-generates the ID)
|
|
394
419
|
const r = await this.ragClient.documents.create({
|
|
395
|
-
|
|
420
|
+
chunks: [text],
|
|
396
421
|
metadata,
|
|
397
422
|
collectionIds: [ingestionInfo.collectionId],
|
|
398
423
|
});
|
|
@@ -661,6 +686,7 @@ For exact constraints (enum/min/max/regex/default), consult the individual field
|
|
|
661
686
|
|
|
662
687
|
// 4) Build text + metadata tailored to FieldMetadata
|
|
663
688
|
const { text, metadata } = this._buildFieldTextAndMetadata(moduleName, modelName, field);
|
|
689
|
+
|
|
664
690
|
// also keep the hash in metadata for audit/debug
|
|
665
691
|
(metadata as any).schemaHash = schemaHash;
|
|
666
692
|
|
|
@@ -566,10 +566,11 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
566
566
|
// TODO Move this logic to field crud managers i.e add a parse method to the field crud manager interface
|
|
567
567
|
switch (fieldType) {
|
|
568
568
|
case SolidFieldType.relation: {
|
|
569
|
-
return await this.populateDtoForRelations(fieldMetadata, record, key, dtoRecord);
|
|
569
|
+
return await this.populateDtoForRelations(fieldMetadata, record, key, dtoRecord);
|
|
570
570
|
}
|
|
571
571
|
case SolidFieldType.date:
|
|
572
|
-
case SolidFieldType.datetime:
|
|
572
|
+
case SolidFieldType.datetime:
|
|
573
|
+
return this.populateDtoForDate(record, key, fieldMetadata, dtoRecord);
|
|
573
574
|
case SolidFieldType.int:
|
|
574
575
|
case SolidFieldType.bigint:
|
|
575
576
|
case SolidFieldType.decimal:
|
|
@@ -579,16 +580,24 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
579
580
|
case SolidFieldType.selectionStatic:
|
|
580
581
|
case SolidFieldType.selectionDynamic:
|
|
581
582
|
return this.populateDtoForSelectionValues(dtoRecord, fieldMetadata, record, key);
|
|
582
|
-
|
|
583
|
+
case SolidFieldType.email:
|
|
584
|
+
case SolidFieldType.shortText:
|
|
585
|
+
case SolidFieldType.longtext:
|
|
586
|
+
case SolidFieldType.richText: {
|
|
587
|
+
dtoRecord[fieldMetadata.name] = record[key] ? String(record[key]).trim() : null; // Trim text fields and set to null if empty
|
|
588
|
+
return dtoRecord;
|
|
589
|
+
}
|
|
590
|
+
default: {
|
|
583
591
|
dtoRecord[fieldMetadata.name] = record[key];
|
|
584
592
|
return dtoRecord;
|
|
593
|
+
}
|
|
585
594
|
}
|
|
586
595
|
}
|
|
587
596
|
|
|
588
597
|
private populateDtoForSelectionValues(dtoRecord: Record<string, any>, fieldMetadata: FieldMetadata, record: Record<string, any>, key: string) {
|
|
589
598
|
const rawValue = record[key];
|
|
590
599
|
|
|
591
|
-
if (rawValue == null) {
|
|
600
|
+
if (rawValue == null || rawValue === '' || (typeof rawValue === 'string' && rawValue.trim() === '')) {
|
|
592
601
|
dtoRecord[fieldMetadata.name] = null;
|
|
593
602
|
return dtoRecord;
|
|
594
603
|
}
|
|
@@ -611,16 +620,28 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
611
620
|
|
|
612
621
|
|
|
613
622
|
private populateDtoForBoolean(dtoRecord: Record<string, any>, fieldMetadata: FieldMetadata, record: Record<string, any>, key: string) {
|
|
614
|
-
const
|
|
615
|
-
if (
|
|
616
|
-
|
|
623
|
+
const cellValue = record[key];
|
|
624
|
+
if (cellValue === null || cellValue === undefined || cellValue === '') {
|
|
625
|
+
dtoRecord[fieldMetadata.name] = null; // If the cell is empty, set the field to null
|
|
626
|
+
return dtoRecord;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// If the cell contains values other than 'true', '1', 'yes', 'false', '0', 'no', throw an error
|
|
630
|
+
const booleanValue = (String(cellValue).toLowerCase() === 'true' || String(cellValue) === '1' || String(cellValue).toLowerCase() === 'yes') ? true : (String(cellValue).toLowerCase() === 'false' || String(cellValue) === '0' || String(cellValue).toLowerCase() === 'no') ? false : null;
|
|
631
|
+
if (booleanValue === null) {
|
|
632
|
+
throw new Error(`Invalid boolean value for cell ${key} with value ${cellValue}`);
|
|
617
633
|
}
|
|
618
634
|
dtoRecord[fieldMetadata.name] = booleanValue;
|
|
619
635
|
return dtoRecord;
|
|
620
636
|
}
|
|
621
637
|
|
|
622
638
|
private populateDtoForNumber(dtoRecord: Record<string, any>, fieldMetadata: FieldMetadata, record: Record<string, any>, key: string) {
|
|
623
|
-
const
|
|
639
|
+
const cellValue = record[key];
|
|
640
|
+
if (cellValue === null || cellValue === undefined || cellValue === '') {
|
|
641
|
+
dtoRecord[fieldMetadata.name] = null; // If the cell is empty, set the field to null
|
|
642
|
+
return dtoRecord;
|
|
643
|
+
}
|
|
644
|
+
const numberValue = Number(cellValue);
|
|
624
645
|
if (isNaN(numberValue)) {
|
|
625
646
|
throw new Error(`Invalid number value for cell ${key} with value ${record[key]}`);
|
|
626
647
|
}
|
|
@@ -630,7 +651,13 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
630
651
|
|
|
631
652
|
private populateDtoForDate(record: Record<string, any>, key: string, fieldMetadata: FieldMetadata, dtoRecord: Record<string, any>) {
|
|
632
653
|
{
|
|
633
|
-
|
|
654
|
+
// Get the cell value
|
|
655
|
+
const cellValue = record[key];
|
|
656
|
+
if (!cellValue) { //If cell is a falsy value (empty)
|
|
657
|
+
dtoRecord[fieldMetadata.name] = null; // If the cell is empty, set the field to null
|
|
658
|
+
return dtoRecord;
|
|
659
|
+
}
|
|
660
|
+
const dateValue = new Date(cellValue);
|
|
634
661
|
if (isNaN(dateValue.getTime())) {
|
|
635
662
|
throw new Error(`Invalid date value for cell ${key} with value ${record[key]}`);
|
|
636
663
|
}
|