From 8e27a67e733c85ec9f20351a7db992fb1f27509c Mon Sep 17 00:00:00 2001 From: chavda-bhavik Date: Wed, 13 Sep 2023 14:57:39 +0530 Subject: [PATCH 1/2] feat: Restructured import architecture to match dynamic columns --- .../app/column/dtos/column-response.dto.ts | 10 ++-- .../app/mapping/dtos/update-columns.dto.ts | 13 +++-- .../api/src/app/mapping/mapping.controller.ts | 55 ++++++++++--------- .../usecases/do-mapping/do-mapping.usecase.ts | 33 ++++------- .../get-mappings/get-mappings.usecase.ts | 11 ++-- apps/api/src/app/mapping/usecases/index.ts | 9 ++- .../rename-file-headings.usecase.ts | 13 +++-- .../update-mappings.usecase.ts | 17 +++--- .../validate-mapping.command.ts | 10 +++- .../validate-mapping.usecase.ts | 31 +++-------- .../usecases/do-review/do-review.usecase.ts | 30 +++++----- .../app/shared/services/file/file.service.ts | 19 +++++-- .../src/app/template/template.controller.ts | 2 +- .../download-sample.usecase.ts | 18 +----- apps/api/src/app/upload/upload.controller.ts | 2 +- .../make-upload-entry.usecase.ts | 41 ++++++++------ .../widget/Phases/Phase2/Phase2.tsx | 7 ++- apps/widget/src/hooks/Phase2/usePhase2.ts | 3 +- .../src/repositories/column/column.entity.ts | 10 ++-- .../src/entities/Mapping/Mapping.interface.ts | 14 ++--- libs/shared/src/types/column/column.types.ts | 7 +++ 21 files changed, 182 insertions(+), 173 deletions(-) diff --git a/apps/api/src/app/column/dtos/column-response.dto.ts b/apps/api/src/app/column/dtos/column-response.dto.ts index eac619751..a804b180f 100644 --- a/apps/api/src/app/column/dtos/column-response.dto.ts +++ b/apps/api/src/app/column/dtos/column-response.dto.ts @@ -17,7 +17,7 @@ export class ColumnResponseDto { description: 'Alternative possible keys of the column', type: Array, }) - alternateKeys: string[]; + alternateKeys?: string[]; @ApiPropertyOptional({ description: 'While true, it Indicates column value should exists in data', @@ -38,20 +38,20 @@ export class ColumnResponseDto { @ApiPropertyOptional({ description: 'Regex if type is Regex', }) - regex: string; + regex?: string; @ApiPropertyOptional({ description: 'Description of Regex', }) - regexDescription: string; + regexDescription?: string; @ApiPropertyOptional({ description: 'List of possible values for column if type is Select', }) - selectValues: string[]; + selectValues?: string[]; @ApiProperty({ description: 'Sequence of column', }) - sequence: number; + sequence?: number; } diff --git a/apps/api/src/app/mapping/dtos/update-columns.dto.ts b/apps/api/src/app/mapping/dtos/update-columns.dto.ts index 7f08a81f6..5b1bdd169 100644 --- a/apps/api/src/app/mapping/dtos/update-columns.dto.ts +++ b/apps/api/src/app/mapping/dtos/update-columns.dto.ts @@ -1,13 +1,18 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsDefined, IsString, IsMongoId, IsOptional } from 'class-validator'; +import { IsDefined, IsString, IsOptional } from 'class-validator'; export class UpdateMappingDto { @ApiProperty({ - description: 'Id of the column', + description: 'key of the column', }) - @IsMongoId() @IsDefined() - _columnId: string; + key: string; + + @ApiProperty({ + description: 'Name of the column', + }) + @IsOptional() + name: string; @ApiProperty({ description: 'Selected Heading for column', diff --git a/apps/api/src/app/mapping/mapping.controller.ts b/apps/api/src/app/mapping/mapping.controller.ts index e79c52463..f9cfc835b 100644 --- a/apps/api/src/app/mapping/mapping.controller.ts +++ b/apps/api/src/app/mapping/mapping.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, Get, Param, ParseArrayPipe, Post, UseGuards } from '@nestjs/common'; import { ApiTags, ApiSecurity, ApiOperation, ApiBody } from '@nestjs/swagger'; -import { ACCESS_KEY_NAME, Defaults, UploadStatusEnum } from '@impler/shared'; -import { MappingEntity } from '@impler/dal'; +import { ACCESS_KEY_NAME, Defaults, ITemplateSchemaItem, UploadStatusEnum } from '@impler/shared'; import { JwtAuthGuard } from '@shared/framework/auth.gaurd'; import { validateNotFound } from '@shared/helpers/common.helper'; @@ -11,14 +10,15 @@ import { ValidateMongoId } from '@shared/validations/valid-mongo-id.validation'; import { GetUploadCommand } from '@shared/usecases/get-upload/get-upload.command'; import { UpdateMappingDto } from './dtos/update-columns.dto'; -import { DoMapping } from './usecases/do-mapping/do-mapping.usecase'; -import { GetMappings } from './usecases/get-mappings/get-mappings.usecase'; -import { DoMappingCommand } from './usecases/do-mapping/do-mapping.command'; -import { UpdateMappings } from './usecases/update-mappings/update-mappings.usecase'; -import { FinalizeUpload } from './usecases/finalize-upload/finalize-upload.usecase'; -import { ValidateMapping } from './usecases/validate-mapping/validate-mapping.usecase'; -import { UpdateMappingCommand } from './usecases/update-mappings/update-mappings.command'; -import { ReanameFileHeadings } from './usecases/rename-file-headings/rename-file-headings.usecase'; +import { + DoMapping, + GetMappings, + FinalizeUpload, + ReanameFileHeadings, + DoMappingCommand, + UpdateMappings, + ValidateMapping, +} from './usecases'; @Controller('/mapping') @ApiTags('Mappings') @@ -29,8 +29,8 @@ export class MappingController { private getUpload: GetUpload, private doMapping: DoMapping, private getMappings: GetMappings, - private updateMappings: UpdateMappings, private finalizeUpload: FinalizeUpload, + private updateMappings: UpdateMappings, private validateMapping: ValidateMapping, private renameFileHeadings: ReanameFileHeadings ) {} @@ -39,7 +39,9 @@ export class MappingController { @ApiOperation({ summary: 'Get mapping information for uploaded file', }) - async getMappingInformation(@Param('uploadId', ValidateMongoId) uploadId: string): Promise[]> { + async getMappingInformation( + @Param('uploadId', ValidateMongoId) uploadId: string + ): Promise[]> { const uploadInformation = await this.getUpload.execute( GetUploadCommand.create({ uploadId, @@ -81,7 +83,7 @@ export class MappingController { const uploadInformation = await this.getUpload.execute( GetUploadCommand.create({ uploadId: _uploadId, - select: 'status', + select: 'status customSchema headings', }) ); @@ -92,22 +94,23 @@ export class MappingController { validateUploadStatus(uploadInformation.status as UploadStatusEnum, [UploadStatusEnum.MAPPING]); // validate mapping data - await this.validateMapping.execute(body, _uploadId); + this.validateMapping.execute( + body, + _uploadId, + JSON.parse(uploadInformation.customSchema), + uploadInformation.headings + ); // save mapping if (Array.isArray(body) && body.length > Defaults.ZERO) { - await this.updateMappings.execute( - body - .filter((columnDataItem) => !!columnDataItem.columnHeading) - .map((updateColumnData) => - UpdateMappingCommand.create({ - _columnId: updateColumnData._columnId, - _uploadId, - columnHeading: updateColumnData.columnHeading, - }) - ), - _uploadId - ); + const templateSchema = await this.getMappings.execute(_uploadId); + templateSchema.forEach((schemaItem: ITemplateSchemaItem, index: number) => { + const foundColumn = body.find((item) => item.key === schemaItem.key); + if (foundColumn) { + templateSchema[index].columnHeading = foundColumn.columnHeading; + } + }); + await this.updateMappings.execute(templateSchema, _uploadId); } const { headings } = await this.renameFileHeadings.execute(_uploadId); diff --git a/apps/api/src/app/mapping/usecases/do-mapping/do-mapping.usecase.ts b/apps/api/src/app/mapping/usecases/do-mapping/do-mapping.usecase.ts index 9ff187810..f4a8b2a3b 100644 --- a/apps/api/src/app/mapping/usecases/do-mapping/do-mapping.usecase.ts +++ b/apps/api/src/app/mapping/usecases/do-mapping/do-mapping.usecase.ts @@ -1,33 +1,32 @@ import { Injectable } from '@nestjs/common'; -import { Defaults, UploadStatusEnum } from '@impler/shared'; -import { ColumnEntity, MappingEntity, MappingRepository, UploadRepository } from '@impler/dal'; +import { UploadRepository } from '@impler/dal'; +import { Defaults, ITemplateSchemaItem, UploadStatusEnum } from '@impler/shared'; import { DoMappingCommand } from './do-mapping.command'; @Injectable() export class DoMapping { - constructor(private mappingRepository: MappingRepository, private uploadRepository: UploadRepository) {} + constructor(private uploadRepository: UploadRepository) {} async execute(command: DoMappingCommand) { const uploadInfo = await this.uploadRepository.findById(command._uploadId, 'customSchema'); - const mapping = this.buildMapping(JSON.parse(uploadInfo.customSchema), command.headings, command._uploadId); - const createdHeadings = await this.mappingRepository.createMany(mapping); - await this.uploadRepository.update({ _id: command._uploadId }, { status: UploadStatusEnum.MAPPING }); + const updatedTemplateSchema = this.buildMapping(JSON.parse(uploadInfo.customSchema), command.headings); + await this.uploadRepository.update( + { _id: command._uploadId }, + { status: UploadStatusEnum.MAPPING, customSchema: JSON.stringify(updatedTemplateSchema) } + ); - return createdHeadings; + return updatedTemplateSchema; } - private buildMapping(columns: ColumnEntity[], headings: string[], _uploadId: string) { - const mappings: MappingEntity[] = []; + private buildMapping(columns: ITemplateSchemaItem[], headings: string[]): ITemplateSchemaItem[] { for (const column of columns) { const heading = this.findBestMatchingHeading(headings, column.key, column.alternateKeys); if (heading) { - mappings.push(this.buildMappingItem(column._id, _uploadId, heading)); - } else { - mappings.push(this.buildMappingItem(column._id, _uploadId)); + column.columnHeading = heading; } } - return mappings; + return columns; } private findBestMatchingHeading(headings: string[], key: string, alternateKeys: string[]): string | null { @@ -50,12 +49,4 @@ export class DoMapping { private checkStringEqual(a: string, b: string): boolean { return String(a).localeCompare(String(b), undefined, { sensitivity: 'accent' }) === Defaults.ZERO; } - - private buildMappingItem(columnId: string, uploadId: string, heading?: string): MappingEntity { - return { - _columnId: columnId, - _uploadId: uploadId, - columnHeading: heading || null, - }; - } } diff --git a/apps/api/src/app/mapping/usecases/get-mappings/get-mappings.usecase.ts b/apps/api/src/app/mapping/usecases/get-mappings/get-mappings.usecase.ts index 5f0e79152..3482cc55b 100644 --- a/apps/api/src/app/mapping/usecases/get-mappings/get-mappings.usecase.ts +++ b/apps/api/src/app/mapping/usecases/get-mappings/get-mappings.usecase.ts @@ -1,11 +1,14 @@ import { Injectable } from '@nestjs/common'; -import { MappingRepository } from '@impler/dal'; +import { UploadRepository } from '@impler/dal'; +import { ITemplateSchemaItem } from '@impler/shared'; @Injectable() export class GetMappings { - constructor(private mappingRepository: MappingRepository) {} + constructor(private uploadRepository: UploadRepository) {} - async execute(_uploadId: string) { - return await this.mappingRepository.getMappingInfo(_uploadId); + async execute(_uploadId: string): Promise { + const uploadInfo = await this.uploadRepository.findById(_uploadId, 'customSchema'); + + return JSON.parse(uploadInfo.customSchema); } } diff --git a/apps/api/src/app/mapping/usecases/index.ts b/apps/api/src/app/mapping/usecases/index.ts index a6b75cabd..b1bd26415 100644 --- a/apps/api/src/app/mapping/usecases/index.ts +++ b/apps/api/src/app/mapping/usecases/index.ts @@ -2,9 +2,12 @@ import { DoMapping } from './do-mapping/do-mapping.usecase'; import { GetMappings } from './get-mappings/get-mappings.usecase'; import { UpdateMappings } from './update-mappings/update-mappings.usecase'; import { FinalizeUpload } from './finalize-upload/finalize-upload.usecase'; +import { GetUpload } from '@shared/usecases/get-upload/get-upload.usecase'; import { ValidateMapping } from './validate-mapping/validate-mapping.usecase'; import { ReanameFileHeadings } from './rename-file-headings/rename-file-headings.usecase'; -import { GetUpload } from '@shared/usecases/get-upload/get-upload.usecase'; + +import { DoMappingCommand } from './do-mapping/do-mapping.command'; +import { ValidateMappingCommand } from './validate-mapping/validate-mapping.command'; export const USE_CASES = [ DoMapping, @@ -16,3 +19,7 @@ export const USE_CASES = [ GetUpload, // ]; + +export { DoMapping, ValidateMapping, GetMappings, UpdateMappings, FinalizeUpload, ReanameFileHeadings, GetUpload }; + +export { DoMappingCommand, ValidateMappingCommand }; diff --git a/apps/api/src/app/mapping/usecases/rename-file-headings/rename-file-headings.usecase.ts b/apps/api/src/app/mapping/usecases/rename-file-headings/rename-file-headings.usecase.ts index 3b522e1c1..afbcda4a4 100644 --- a/apps/api/src/app/mapping/usecases/rename-file-headings/rename-file-headings.usecase.ts +++ b/apps/api/src/app/mapping/usecases/rename-file-headings/rename-file-headings.usecase.ts @@ -1,19 +1,20 @@ import { Injectable } from '@nestjs/common'; -import { MappingRepository, UploadRepository } from '@impler/dal'; +import { UploadRepository } from '@impler/dal'; +import { ITemplateSchemaItem } from '@impler/shared'; @Injectable() export class ReanameFileHeadings { - constructor(private uploadRepository: UploadRepository, private mappingRepository: MappingRepository) {} + constructor(private uploadRepository: UploadRepository) {} async execute(_uploadId: string): Promise<{ headings: string[] }> { return new Promise(async (resolve, reject) => { try { - const uploadInfo = await this.uploadRepository.findById(_uploadId, 'headings _uploadedFileId'); - const mappingInfo = await this.mappingRepository.getMappingWithColumnInfo(_uploadId); + const uploadInfo = await this.uploadRepository.findById(_uploadId, 'headings _uploadedFileId customSchema'); + const templateColumnItems = JSON.parse(uploadInfo.customSchema) as ITemplateSchemaItem[]; const newHeadings = uploadInfo.headings.reduce((headingsArr, heading) => { - const foundMapping = mappingInfo.find((mapping) => mapping.columnHeading === heading); - if (foundMapping) headingsArr.push(foundMapping.column.key); + const foundMapping = templateColumnItems.find((mapping) => mapping.columnHeading === heading); + if (foundMapping) headingsArr.push(foundMapping.key); else headingsArr.push(heading); return headingsArr; diff --git a/apps/api/src/app/mapping/usecases/update-mappings/update-mappings.usecase.ts b/apps/api/src/app/mapping/usecases/update-mappings/update-mappings.usecase.ts index 5a2bc0fb7..191481099 100644 --- a/apps/api/src/app/mapping/usecases/update-mappings/update-mappings.usecase.ts +++ b/apps/api/src/app/mapping/usecases/update-mappings/update-mappings.usecase.ts @@ -1,14 +1,17 @@ import { Injectable } from '@nestjs/common'; -import { MappingRepository } from '@impler/dal'; -import { UpdateMappingCommand } from './update-mappings.command'; +import { UploadRepository } from '@impler/dal'; +import { ITemplateSchemaItem } from '@impler/shared'; @Injectable() export class UpdateMappings { - constructor(private mappingRepository: MappingRepository) {} + constructor(private uploadRepository: UploadRepository) {} - async execute(command: UpdateMappingCommand[], _uploadId: string) { - await this.mappingRepository.deleteMany({ _uploadId }); - - return this.mappingRepository.createMany(command); + async execute(templateSchema: ITemplateSchemaItem[], _uploadId: string) { + await this.uploadRepository.update( + { + _id: _uploadId, + }, + { customSchema: JSON.stringify(templateSchema) } + ); } } diff --git a/apps/api/src/app/mapping/usecases/validate-mapping/validate-mapping.command.ts b/apps/api/src/app/mapping/usecases/validate-mapping/validate-mapping.command.ts index 9ddf96737..4fa920e52 100644 --- a/apps/api/src/app/mapping/usecases/validate-mapping/validate-mapping.command.ts +++ b/apps/api/src/app/mapping/usecases/validate-mapping/validate-mapping.command.ts @@ -1,10 +1,14 @@ -import { IsString, IsDefined, IsMongoId } from 'class-validator'; +import { IsString, IsDefined } from 'class-validator'; import { BaseCommand } from '@shared/commands/base.command'; export class ValidateMappingCommand extends BaseCommand { @IsDefined() - @IsMongoId() - _columnId: string; + @IsString() + key: string; + + @IsDefined() + @IsString() + name: string; @IsDefined() @IsString() diff --git a/apps/api/src/app/mapping/usecases/validate-mapping/validate-mapping.usecase.ts b/apps/api/src/app/mapping/usecases/validate-mapping/validate-mapping.usecase.ts index edee84ce7..135b25251 100644 --- a/apps/api/src/app/mapping/usecases/validate-mapping/validate-mapping.usecase.ts +++ b/apps/api/src/app/mapping/usecases/validate-mapping/validate-mapping.usecase.ts @@ -1,39 +1,24 @@ import { BadRequestException, Injectable } from '@nestjs/common'; -import { ColumnRepository, UploadRepository } from '@impler/dal'; import { ValidateMappingCommand } from './validate-mapping.command'; +import { ITemplateSchemaItem } from '@impler/shared'; + @Injectable() export class ValidateMapping { - constructor(private columnRepository: ColumnRepository, private uploadRepository: UploadRepository) {} - - async execute(command: ValidateMappingCommand[], _uploadId: string) { + execute(command: ValidateMappingCommand[], _uploadId: string, columns: ITemplateSchemaItem[], headings: string[]) { // check if mapping data contains duplicates - const mappings = [...new Map(command.map((item) => [item._columnId, item])).values()]; + const mappings = [...new Map(command.map((item) => [item.key, item])).values()]; if (mappings.length !== command.length) throw new BadRequestException('Mapping data contains duplicates'); - // Check if mapping data _columnIds are valid - const columnIds = command.map((mapping) => ({ - _id: mapping._columnId, - })); - const columnEntities = await this.columnRepository.find( - { - $or: [...columnIds], - }, - 'name _id isRequired' - ); - if (columnEntities.length !== command.length) - throw new BadRequestException(`Mapping data contains invalid _columnId(s)`); - // check if mapping data headings are valid const columnHeadings = command.map((mapping) => mapping.columnHeading).filter((heading) => !!heading); - const uploadInfo = await this.uploadRepository.findById(_uploadId, 'headings'); - const isAllHeadingsAreValid = columnHeadings.every((heading) => uploadInfo.headings.includes(heading)); + const isAllHeadingsAreValid = columnHeadings.every((heading) => headings.includes(heading)); if (!isAllHeadingsAreValid) throw new BadRequestException(`Mapping data contains invalid columnHeading values`); // check if mapping data has required columns - const columnEntityIds = command.filter((item) => !!item.columnHeading).map((item) => item._columnId); - for (const columnEntity of columnEntities) { - if (columnEntity.isRequired && !columnEntityIds.includes(columnEntity._id)) { + const providedColumnKeys = command.filter((item) => !!item.columnHeading).map((item) => item.key); + for (const columnEntity of columns) { + if (columnEntity.isRequired && !providedColumnKeys.includes(columnEntity.key)) { throw new BadRequestException(`${columnEntity.name} is required`); } } diff --git a/apps/api/src/app/review/usecases/do-review/do-review.usecase.ts b/apps/api/src/app/review/usecases/do-review/do-review.usecase.ts index 8b7e2ccfa..c1b0ba557 100644 --- a/apps/api/src/app/review/usecases/do-review/do-review.usecase.ts +++ b/apps/api/src/app/review/usecases/do-review/do-review.usecase.ts @@ -8,8 +8,8 @@ import { Injectable, BadRequestException } from '@nestjs/common'; import Ajv, { AnySchemaObject, ErrorObject, ValidateFunction } from 'ajv'; import { StorageService } from '@impler/shared/dist/services/storage'; -import { ColumnTypesEnum, FileMimeTypesEnum, UploadStatusEnum } from '@impler/shared'; -import { UploadRepository, MappingRepository, ColumnEntity, ValidatorRepository, FileRepository } from '@impler/dal'; +import { UploadRepository, ValidatorRepository, FileRepository } from '@impler/dal'; +import { ColumnTypesEnum, FileMimeTypesEnum, ITemplateSchemaItem, UploadStatusEnum } from '@impler/shared'; import { APIMessages } from '@shared/constants'; import { FileNameService } from '@shared/services'; @@ -75,7 +75,6 @@ export class DoReview { constructor( private uploadRepository: UploadRepository, private storageService: StorageService, - private mappingRepository: MappingRepository, private validatorRepository: ValidatorRepository, private fileNameService: FileNameService, private fileRepository: FileRepository, @@ -87,8 +86,7 @@ export class DoReview { if (!uploadInfo) { throw new BadRequestException(APIMessages.UPLOAD_NOT_FOUND); } - const mappings = await this.mappingRepository.getMappingWithColumnInfo(_uploadId); - const schema = this.buildAJVSchema(JSON.parse(uploadInfo.customSchema), mappings); + const schema = this.buildAJVSchema(JSON.parse(uploadInfo.customSchema)); const validator = ajv.compile(schema); const uploadedFileInfo = await this.fileRepository.findById(uploadInfo._uploadedFileId); @@ -126,27 +124,27 @@ export class DoReview { return response; } - private buildAJVSchema(columns: ColumnEntity[], mappings: any[]) { - const formattedColumns: Record = columns.reduce((acc, column) => { - acc[column._id] = { ...column }; + private buildAJVSchema(columns: ITemplateSchemaItem[]) { + const formattedColumns: Record = columns.reduce((acc, column) => { + acc[column.key] = { ...column }; return acc; }, {}); - const properties: Record = mappings.reduce((acc, mapping) => { - acc[mapping.column.key] = this.getProperty(formattedColumns[mapping.column._columnId]); + const properties: Record = columns.reduce((acc, column) => { + acc[column.key] = this.getProperty(formattedColumns[column.key]); return acc; }, {}); - const requiredProperties: string[] = mappings.reduce((acc, mapping) => { - if (formattedColumns[mapping.column._columnId].isRequired) acc.push(mapping.column.key); + const requiredProperties: string[] = columns.reduce((acc, column) => { + if (formattedColumns[column.key].isRequired) acc.push(column.key); return acc; }, []); // setting uniqueItems to empty set to avoid error - mappings.forEach((mapping) => { - if (formattedColumns[mapping.column._columnId].isUnique) { - uniqueItems[mapping.column.key] = new Set(); + columns.forEach((column) => { + if (formattedColumns[column.key].isUnique) { + uniqueItems[column.key] = new Set(); } }); @@ -158,7 +156,7 @@ export class DoReview { }; } - private getProperty(column: ColumnEntity): Record { + private getProperty(column: ITemplateSchemaItem): Record { let property: Record = {}; switch (column.type) { diff --git a/apps/api/src/app/shared/services/file/file.service.ts b/apps/api/src/app/shared/services/file/file.service.ts index 0180e5bee..f0d1399e2 100644 --- a/apps/api/src/app/shared/services/file/file.service.ts +++ b/apps/api/src/app/shared/services/file/file.service.ts @@ -153,14 +153,16 @@ export class CSVFileService2 { } else { fileContent = file.buffer.toString(FileEncodingsEnum.CSV); } + let headings: string[]; + let recordIndex = -1; parse(fileContent, { ...(options || {}), - preview: 1, + preview: 2, step: (results) => { - if (Object.keys(results.data).length > Defaults.ONE) { - resolve(results.data); - } else { - reject(new EmptyFileException()); + recordIndex++; + if (recordIndex === Defaults.ZERO) { + if (Array.isArray(results.data) && results.data.length > Defaults.ZERO) headings = results.data as string[]; + else reject(new EmptyFileException()); } }, error: (error) => { @@ -170,6 +172,13 @@ export class CSVFileService2 { reject(error); } }, + complete: () => { + if (recordIndex !== Defaults.ONE) { + reject(new EmptyFileException()); + } else { + resolve(headings); + } + }, }); }); } diff --git a/apps/api/src/app/template/template.controller.ts b/apps/api/src/app/template/template.controller.ts index bbdbadb5b..99d127acb 100644 --- a/apps/api/src/app/template/template.controller.ts +++ b/apps/api/src/app/template/template.controller.ts @@ -95,7 +95,7 @@ export class TemplateController { summary: 'Get template columns', }) @ApiOkResponse({ - type: [TemplateResponseDto], + type: [ColumnResponseDto], }) async getColumns(@Param('templateId') _templateId: string): Promise { return this.getTemplateColumns.execute(_templateId); diff --git a/apps/api/src/app/template/usecases/download-sample/download-sample.usecase.ts b/apps/api/src/app/template/usecases/download-sample/download-sample.usecase.ts index d11a007d1..64a8486a2 100644 --- a/apps/api/src/app/template/usecases/download-sample/download-sample.usecase.ts +++ b/apps/api/src/app/template/usecases/download-sample/download-sample.usecase.ts @@ -1,9 +1,8 @@ import { Injectable } from '@nestjs/common'; -import { ColumnEntity, ColumnRepository } from '@impler/dal'; +import { ColumnRepository } from '@impler/dal'; import { ColumnTypesEnum, ISchemaItem } from '@impler/shared'; import { ExcelFileService } from '@shared/services/file'; -import { mergeObjects } from '@shared/helpers/common.helper'; import { IExcelFileHeading } from '@shared/types/file.types'; import { DownloadSampleCommand } from './download-sample.command'; @@ -25,20 +24,9 @@ export class DownloadSample { } catch (error) {} if (Array.isArray(parsedSchema) && parsedSchema.length > 0) { - // if custom schema is provided, merge it with default schema - const formattedColumns: Record = columns.reduce((acc, column) => { - acc[column.key] = { ...column }; - - return acc; - }, {}); - parsedSchema.forEach((schemaItem) => { - if (formattedColumns.hasOwnProperty(schemaItem.key)) { - mergeObjects(formattedColumns[schemaItem.key], schemaItem, ['isRequired', 'selectValues', 'type']); - } - }); - columnKeys = Object.values(formattedColumns).map((columnItem) => ({ + columnKeys = parsedSchema.map((columnItem) => ({ key: columnItem.key, - type: columnItem.type as ColumnTypesEnum, + type: (columnItem.type as ColumnTypesEnum) || ColumnTypesEnum.STRING, selectValues: columnItem.selectValues, isRequired: columnItem.isRequired, })); diff --git a/apps/api/src/app/upload/upload.controller.ts b/apps/api/src/app/upload/upload.controller.ts index e7e7a52f3..1706bc3bb 100644 --- a/apps/api/src/app/upload/upload.controller.ts +++ b/apps/api/src/app/upload/upload.controller.ts @@ -51,7 +51,7 @@ export class UploadController { @Body() body: UploadRequestDto, @Param('templateId', ValidateTemplate) templateId: string ) { - return await this.makeUploadEntry.execute( + return this.makeUploadEntry.execute( MakeUploadEntryCommand.create({ file: file, templateId, diff --git a/apps/api/src/app/upload/usecases/make-upload-entry/make-upload-entry.usecase.ts b/apps/api/src/app/upload/usecases/make-upload-entry/make-upload-entry.usecase.ts index af984a28c..6babac262 100644 --- a/apps/api/src/app/upload/usecases/make-upload-entry/make-upload-entry.usecase.ts +++ b/apps/api/src/app/upload/usecases/make-upload-entry/make-upload-entry.usecase.ts @@ -1,7 +1,13 @@ import { Injectable } from '@nestjs/common'; -import { FileMimeTypesEnum, UploadStatusEnum, Defaults, ISchemaItem } from '@impler/shared'; import { - ColumnEntity, + FileMimeTypesEnum, + UploadStatusEnum, + Defaults, + ISchemaItem, + ITemplateSchemaItem, + ColumnTypesEnum, +} from '@impler/shared'; +import { ColumnRepository, CommonRepository, FileEntity, @@ -10,7 +16,6 @@ import { UploadRepository, } from '@impler/dal'; -import { mergeObjects } from '@shared/helpers/common.helper'; import { AddUploadEntryCommand } from './add-upload-entry.command'; import { MakeUploadEntryCommand } from './make-upload-entry.command'; import { StorageService } from '@impler/shared/dist/services/storage'; @@ -44,7 +49,7 @@ export class MakeUploadEntry { { _templateId: templateId, }, - 'key isRequired isUnique selectValues type regex sequence', + 'name key isRequired isUnique selectValues type regex sequence', { sort: 'sequence', } @@ -54,21 +59,21 @@ export class MakeUploadEntry { if (schema) parsedSchema = JSON.parse(schema); } catch (error) {} if (Array.isArray(parsedSchema) && parsedSchema.length > 0) { - const formattedColumns: Record = columns.reduce((acc, column) => { - acc[column.key] = { ...column }; - - return acc; - }, {}); + const formattedColumns: Record> = {}; parsedSchema.forEach((schemaItem) => { - if (formattedColumns.hasOwnProperty(schemaItem.key)) { - mergeObjects(formattedColumns[schemaItem.key], schemaItem, [ - 'isRequired', - 'isUnique', - 'selectValues', - 'type', - 'regex', - ]); - } + // overrides duplicate + formattedColumns[schemaItem.key] = { + name: schemaItem.name || 'Untitled Column', + isRequired: schemaItem.isRequired || false, + key: schemaItem.key, + type: schemaItem.type || ColumnTypesEnum.STRING, + regex: schemaItem.regex, + selectValues: schemaItem.selectValues || [], + isUnique: schemaItem.isUnique || false, + + sequence: Object.keys(formattedColumns).length, + columnHeading: '', + }; }); combinedSchema = JSON.stringify(Object.values(formattedColumns)); } else { diff --git a/apps/widget/src/components/widget/Phases/Phase2/Phase2.tsx b/apps/widget/src/components/widget/Phases/Phase2/Phase2.tsx index e8ae90f74..fb57eb108 100644 --- a/apps/widget/src/components/widget/Phases/Phase2/Phase2.tsx +++ b/apps/widget/src/components/widget/Phases/Phase2/Phase2.tsx @@ -47,14 +47,15 @@ export function Phase2(props: IPhase2Props) { {Array.isArray(mappings) && mappings.map((mappingItem, index) => ( ( { field.onChange(value); diff --git a/apps/widget/src/hooks/Phase2/usePhase2.ts b/apps/widget/src/hooks/Phase2/usePhase2.ts index b593e7117..818e38383 100644 --- a/apps/widget/src/hooks/Phase2/usePhase2.ts +++ b/apps/widget/src/hooks/Phase2/usePhase2.ts @@ -45,7 +45,8 @@ export function usePhase2({ goNext }: IUsePhase2Props) { ); reset({ mappings: mappingsResponse.map((mapping) => ({ - _columnId: mapping.column._columnId, + key: mapping.key, + name: mapping.name, columnHeading: mapping.columnHeading, })), }); diff --git a/libs/dal/src/repositories/column/column.entity.ts b/libs/dal/src/repositories/column/column.entity.ts index 47ba5aa3b..b20450eb9 100644 --- a/libs/dal/src/repositories/column/column.entity.ts +++ b/libs/dal/src/repositories/column/column.entity.ts @@ -5,7 +5,7 @@ export class ColumnEntity { key: string; - alternateKeys: string[]; + alternateKeys?: string[]; isRequired: boolean; @@ -13,13 +13,13 @@ export class ColumnEntity { type: string; - regex: string; + regex?: string; - regexDescription: string; + regexDescription?: string; - selectValues: string[]; + selectValues?: string[]; - sequence: number; + sequence?: number; _templateId: string; } diff --git a/libs/shared/src/entities/Mapping/Mapping.interface.ts b/libs/shared/src/entities/Mapping/Mapping.interface.ts index 8d783c3bf..229f0c9b6 100644 --- a/libs/shared/src/entities/Mapping/Mapping.interface.ts +++ b/libs/shared/src/entities/Mapping/Mapping.interface.ts @@ -1,14 +1,12 @@ export interface IMapping { - _id: string; - column: { - _columnId: string; - name: string; - sequence: number; - }; - columnHeading?: string; + name: string; + key: string; + columnHeading: string; + isRequired: boolean; } export interface IMappingFinalize { - _columnId: string; + name: string; + key: string; columnHeading: string; } diff --git a/libs/shared/src/types/column/column.types.ts b/libs/shared/src/types/column/column.types.ts index d8caec65a..a6472437c 100644 --- a/libs/shared/src/types/column/column.types.ts +++ b/libs/shared/src/types/column/column.types.ts @@ -10,9 +10,16 @@ export enum ColumnTypesEnum { export interface ISchemaItem { key: string; + name: string; + alternateKeys?: string[]; isRequired?: boolean; isUnique?: boolean; selectValues?: string[]; type?: ColumnTypesEnum; regex?: string; } + +export interface ITemplateSchemaItem extends ISchemaItem { + sequence: number; + columnHeading: string; +} From 4468b1e82c4a418f9d9e1ac3fbc1888f23656ac4 Mon Sep 17 00:00:00 2001 From: chavda-bhavik Date: Wed, 13 Sep 2023 15:01:04 +0530 Subject: [PATCH 2/2] feat: Replaced Typescript Or with Enum for schema column, for better intellisense --- packages/react/src/components/button/Button.types.ts | 11 +++++++++++ packages/react/src/hooks/useImpler.ts | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/react/src/components/button/Button.types.ts b/packages/react/src/components/button/Button.types.ts index 1ea691ee8..b8608e5df 100644 --- a/packages/react/src/components/button/Button.types.ts +++ b/packages/react/src/components/button/Button.types.ts @@ -33,6 +33,17 @@ export enum EventTypesEnum { UPLOAD_COMPLETED = 'UPLOAD_COMPLETED', } +export interface ISchemaItem { + key: string; + name: string; + alternateKeys?: string[]; + isRequired?: boolean; + isUnique?: boolean; + selectValues?: string[]; + type?: 'String' | 'Number' | 'Date' | 'Email' | 'Regex' | 'Select' | 'Any'; + regex?: string; +} + export type EventCalls = | { type: EventTypesEnum.UPLOAD_STARTED; diff --git a/packages/react/src/hooks/useImpler.ts b/packages/react/src/hooks/useImpler.ts index b74316bc1..56abff045 100644 --- a/packages/react/src/hooks/useImpler.ts +++ b/packages/react/src/hooks/useImpler.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useState } from 'react'; import { logError } from '../utils/logger'; -import { EventTypesEnum, IShowPayload, IUpload, ISchemaItem } from '@impler/shared'; -import { EventCalls, UploadTemplateData, UploadData } from '../components/button/Button.types'; +import { EventTypesEnum, IShowPayload, IUpload } from '@impler/shared'; +import { EventCalls, UploadTemplateData, UploadData, ISchemaItem } from '../components/button/Button.types'; interface ShowWidgetProps { colorScheme?: 'light' | 'dark';