diff --git a/apps/api/src/app/mapping/mapping.controller.ts b/apps/api/src/app/mapping/mapping.controller.ts index f2f6aefb8..4ec45512e 100644 --- a/apps/api/src/app/mapping/mapping.controller.ts +++ b/apps/api/src/app/mapping/mapping.controller.ts @@ -5,16 +5,18 @@ import { MappingEntity } from '@impler/dal'; import { APIKeyGuard } from '../shared/framework/auth.gaurd'; import { ValidateMongoId } from '../shared/validations/valid-mongo-id.validation'; -import { GetUploadCommand } from './../upload/usecases/get-upload/get-upload.command'; +import { GetUploadCommand } from '../shared/usecases/get-upload/get-upload.command'; import { DoMapping } from './usecases/do-mapping/do-mapping.usecase'; import { DoMappingCommand } from './usecases/do-mapping/do-mapping.command'; -import { GetUpload } from './../upload/usecases/get-upload/get-upload.usecase'; +import { GetUpload } from '../shared/usecases/get-upload/get-upload.usecase'; import { GetMappings } from './usecases/get-mappings/get-mappings.usecase'; import { UpdateMappingCommand } from './usecases/update-mappings/update-mappings.command'; import { UpdateMappings } from './usecases/update-mappings/update-mappings.usecase'; import { FinalizeUpload } from './usecases/finalize-upload/finalize-upload.usecase'; import { UpdateMappingDto } from './dtos/update-columns.dto'; import { ValidateMapping } from './usecases/validate-mapping/validate-mapping.usecase'; +import { validateUploadStatus } from '../shared/helpers/upload.helpers'; +import { validateNotFound } from '../shared/helpers/common.helper'; @Controller('/mapping') @ApiTags('Mappings') @@ -42,6 +44,15 @@ export class MappingController { }) ); + // throw error if upload information not found + validateNotFound(uploadInformation, 'upload'); + + // Get mappings can be called only when file is uploaded or it's mapping in progress + validateUploadStatus(uploadInformation.status as UploadStatusEnum, [ + UploadStatusEnum.UPLOADED, + UploadStatusEnum.MAPPING, + ]); + if (uploadInformation.status === UploadStatusEnum.UPLOADED) { await this.doMapping.execute( DoMappingCommand.create({ @@ -64,6 +75,19 @@ export class MappingController { @Param('uploadId', ValidateMongoId) _uploadId: string, @Body(new ParseArrayPipe({ items: UpdateMappingDto, optional: true })) body: UpdateMappingDto[] ) { + const uploadInformation = await this.getUpload.execute( + GetUploadCommand.create({ + uploadId: _uploadId, + select: 'status', + }) + ); + + // throw error if upload information not found + validateNotFound(uploadInformation, 'upload'); + + // Finalize mapping can only be called after the mapping has been completed + validateUploadStatus(uploadInformation.status as UploadStatusEnum, [UploadStatusEnum.MAPPING]); + // validate mapping data await this.validateMapping.execute(body, _uploadId); diff --git a/apps/api/src/app/mapping/mapping.module.ts b/apps/api/src/app/mapping/mapping.module.ts index 160126f06..02443ad4a 100644 --- a/apps/api/src/app/mapping/mapping.module.ts +++ b/apps/api/src/app/mapping/mapping.module.ts @@ -1,12 +1,11 @@ import { Module } from '@nestjs/common'; import { USE_CASES } from './usecases'; -import { GetUpload } from './../upload/usecases/get-upload/get-upload.usecase'; import { MappingController } from './mapping.controller'; import { SharedModule } from '../shared/shared.module'; @Module({ imports: [SharedModule], - providers: [...USE_CASES, GetUpload], + providers: [...USE_CASES], controllers: [MappingController], }) export class MappingModule {} diff --git a/apps/api/src/app/mapping/usecases/index.ts b/apps/api/src/app/mapping/usecases/index.ts index d7f20480e..f1557f27e 100644 --- a/apps/api/src/app/mapping/usecases/index.ts +++ b/apps/api/src/app/mapping/usecases/index.ts @@ -3,6 +3,7 @@ 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 { ValidateMapping } from './validate-mapping/validate-mapping.usecase'; +import { GetUpload } from '../../shared/usecases/get-upload/get-upload.usecase'; export const USE_CASES = [ DoMapping, @@ -10,5 +11,6 @@ export const USE_CASES = [ UpdateMappings, FinalizeUpload, ValidateMapping, + GetUpload, // ]; diff --git a/apps/api/src/app/review/dtos/confirm-review-request.dto.ts b/apps/api/src/app/review/dtos/confirm-review-request.dto.ts new file mode 100644 index 000000000..040edc4ef --- /dev/null +++ b/apps/api/src/app/review/dtos/confirm-review-request.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class ConfirmReviewRequestDto { + @ApiProperty({ + description: 'Boolean value indicating whether to process the invalid data or not.', + default: false, + required: false, + }) + @IsBoolean() + @IsOptional() + processInvalidRecords: boolean; +} diff --git a/apps/api/src/app/review/review.controller.ts b/apps/api/src/app/review/review.controller.ts index 75ad39183..f528484c1 100644 --- a/apps/api/src/app/review/review.controller.ts +++ b/apps/api/src/app/review/review.controller.ts @@ -1,6 +1,6 @@ -import { BadRequestException, Controller, Get, Param, UseGuards } from '@nestjs/common'; +import { BadRequestException, Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common'; import { ApiOperation, ApiTags, ApiSecurity } from '@nestjs/swagger'; -import { FileEntity } from '@impler/dal'; +import { FileEntity, UploadEntity } from '@impler/dal'; import { UploadStatusEnum } from '@impler/shared'; import { APIMessages } from '../shared/constants'; import { APIKeyGuard } from '../shared/framework/auth.gaurd'; @@ -9,6 +9,13 @@ import { DoReview } from './usecases/do-review/do-review.usecase'; import { GetUploadInvalidData } from './usecases/get-upload-invalid-data/get-upload-invalid-data.usecase'; import { SaveReviewData } from './usecases/save-review-data/save-review-data.usecase'; import { GetFileInvalidData } from './usecases/get-file-invalid-data/get-file-invalid-data.usecase'; +import { ValidateMongoId } from '../shared/validations/valid-mongo-id.validation'; +import { ConfirmReviewRequestDto } from './dtos/confirm-review-request.dto'; +import { GetUploadCommand } from '../shared/usecases/get-upload/get-upload.command'; +import { GetUpload } from '../shared/usecases/get-upload/get-upload.usecase'; +import { validateNotFound } from '../shared/helpers/common.helper'; +import { ConfirmReview } from './usecases/confirm-review/confirm-review.usecase'; +import { ConfirmReviewCommand } from './usecases/confirm-review/confirm-review.command'; @Controller('/review') @ApiTags('Review') @@ -17,6 +24,8 @@ import { GetFileInvalidData } from './usecases/get-file-invalid-data/get-file-in export class ReviewController { constructor( private doReview: DoReview, + private getUpload: GetUpload, + private confirmReview: ConfirmReview, private saveReviewData: SaveReviewData, private getFileInvalidData: GetFileInvalidData, private getUploadInvalidData: GetUploadInvalidData @@ -45,4 +54,33 @@ export class ReviewController { return this.getFileInvalidData.execute((uploadData._invalidDataFileId as unknown as FileEntity).path); } } + + @Post(':uploadId/confirm') + @ApiOperation({ + summary: 'Confirm review data for uploaded file', + }) + async doConfirmReview( + @Param('uploadId', ValidateMongoId) _uploadId: string, + @Body() body: ConfirmReviewRequestDto + ): Promise { + const uploadInformation = await this.getUpload.execute( + GetUploadCommand.create({ + uploadId: _uploadId, + select: 'status', + }) + ); + + // throw error if upload information not found + validateNotFound(uploadInformation, 'upload'); + + // upload files with status reviewing can only be confirmed + validateUploadStatus(uploadInformation.status as UploadStatusEnum, [UploadStatusEnum.REVIEWING]); + + return this.confirmReview.execute( + ConfirmReviewCommand.create({ + _uploadId: _uploadId, + processInvalidRecords: body.processInvalidRecords, + }) + ); + } } diff --git a/apps/api/src/app/review/usecases/confirm-review/confirm-review.command.ts b/apps/api/src/app/review/usecases/confirm-review/confirm-review.command.ts new file mode 100644 index 000000000..80e0b97c5 --- /dev/null +++ b/apps/api/src/app/review/usecases/confirm-review/confirm-review.command.ts @@ -0,0 +1,12 @@ +import { IsBoolean, IsDefined, IsMongoId } from 'class-validator'; +import { BaseCommand } from '../../../shared/commands/base.command'; + +export class ConfirmReviewCommand extends BaseCommand { + @IsDefined() + @IsMongoId() + _uploadId: string; + + @IsDefined() + @IsBoolean() + processInvalidRecords: boolean; +} diff --git a/apps/api/src/app/review/usecases/confirm-review/confirm-review.usecase.ts b/apps/api/src/app/review/usecases/confirm-review/confirm-review.usecase.ts new file mode 100644 index 000000000..681db332f --- /dev/null +++ b/apps/api/src/app/review/usecases/confirm-review/confirm-review.usecase.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@nestjs/common'; +import { UploadEntity, UploadRepository } from '@impler/dal'; +import { ConfirmReviewCommand } from './confirm-review.command'; +import { UploadStatusEnum } from '@impler/shared'; + +@Injectable() +export class ConfirmReview { + constructor(private uploadRepository: UploadRepository) {} + + execute(command: ConfirmReviewCommand): Promise { + return this.uploadRepository.findOneAndUpdate( + { _id: command._uploadId }, + { status: UploadStatusEnum.CONFIRMED, processInvalidRecords: command.processInvalidRecords } + ); + } +} diff --git a/apps/api/src/app/review/usecases/index.ts b/apps/api/src/app/review/usecases/index.ts index 85a02fe90..aa8c1650f 100644 --- a/apps/api/src/app/review/usecases/index.ts +++ b/apps/api/src/app/review/usecases/index.ts @@ -2,9 +2,13 @@ import { DoReview } from './do-review/do-review.usecase'; import { SaveReviewData } from './save-review-data/save-review-data.usecase'; import { GetUploadInvalidData } from './get-upload-invalid-data/get-upload-invalid-data.usecase'; import { GetFileInvalidData } from './get-file-invalid-data/get-file-invalid-data.usecase'; +import { GetUpload } from '../../shared/usecases/get-upload/get-upload.usecase'; +import { ConfirmReview } from './confirm-review/confirm-review.usecase'; export const USE_CASES = [ DoReview, + GetUpload, + ConfirmReview, SaveReviewData, GetFileInvalidData, GetUploadInvalidData, diff --git a/apps/api/src/app/shared/helpers/common.helper.ts b/apps/api/src/app/shared/helpers/common.helper.ts new file mode 100644 index 000000000..22fda0599 --- /dev/null +++ b/apps/api/src/app/shared/helpers/common.helper.ts @@ -0,0 +1,14 @@ +import { BadRequestException } from '@nestjs/common'; +import { APIMessages } from '../constants'; + +export function validateNotFound(data: any, entityName: 'upload'): boolean { + if (data) return true; + else { + switch (entityName) { + case 'upload': + throw new BadRequestException(APIMessages.UPLOAD_NOT_FOUND); + default: + throw new BadRequestException(); + } + } +} diff --git a/apps/api/src/app/upload/usecases/get-upload/get-upload.command.ts b/apps/api/src/app/shared/usecases/get-upload/get-upload.command.ts similarity index 77% rename from apps/api/src/app/upload/usecases/get-upload/get-upload.command.ts rename to apps/api/src/app/shared/usecases/get-upload/get-upload.command.ts index 31f51fc08..9abd8f9b8 100644 --- a/apps/api/src/app/upload/usecases/get-upload/get-upload.command.ts +++ b/apps/api/src/app/shared/usecases/get-upload/get-upload.command.ts @@ -1,5 +1,5 @@ import { IsDefined, IsMongoId, IsOptional, IsString } from 'class-validator'; -import { BaseCommand } from '../../../shared/commands/base.command'; +import { BaseCommand } from '../../commands/base.command'; export class GetUploadCommand extends BaseCommand { @IsDefined() diff --git a/apps/api/src/app/upload/usecases/get-upload/get-upload.usecase.ts b/apps/api/src/app/shared/usecases/get-upload/get-upload.usecase.ts similarity index 100% rename from apps/api/src/app/upload/usecases/get-upload/get-upload.usecase.ts rename to apps/api/src/app/shared/usecases/get-upload/get-upload.usecase.ts diff --git a/apps/api/src/app/upload/upload.controller.ts b/apps/api/src/app/upload/upload.controller.ts index f48ad9281..6ddd15df8 100644 --- a/apps/api/src/app/upload/upload.controller.ts +++ b/apps/api/src/app/upload/upload.controller.ts @@ -11,8 +11,8 @@ import { ValidImportFile } from '../shared/validations/valid-import-file.validat import { MakeUploadEntry } from './usecases/make-upload-entry/make-upload-entry.usecase'; import { MakeUploadEntryCommand } from './usecases/make-upload-entry/make-upload-entry.command'; import { ValidateMongoId } from '../shared/validations/valid-mongo-id.validation'; -import { GetUpload } from './usecases/get-upload/get-upload.usecase'; -import { GetUploadCommand } from './usecases/get-upload/get-upload.command'; +import { GetUpload } from '../shared/usecases/get-upload/get-upload.usecase'; +import { GetUploadCommand } from '../shared/usecases/get-upload/get-upload.command'; import { GetUploads } from './usecases/get-uploads/get-uploads.usecase'; import { GetUploadsCommand } from './usecases/get-uploads/get-uploads.command'; import { ValidateTemplate } from '../shared/validations/valid-template.validation'; diff --git a/apps/api/src/app/upload/usecases/index.ts b/apps/api/src/app/upload/usecases/index.ts index 00fbf4ef9..eb307b79c 100644 --- a/apps/api/src/app/upload/usecases/index.ts +++ b/apps/api/src/app/upload/usecases/index.ts @@ -1,5 +1,5 @@ import { MakeUploadEntry } from './make-upload-entry/make-upload-entry.usecase'; -import { GetUpload } from './get-upload/get-upload.usecase'; +import { GetUpload } from '../../shared/usecases/get-upload/get-upload.usecase'; import { GetUploads } from './get-uploads/get-uploads.usecase'; export const USE_CASES = [ diff --git a/libs/dal/src/repositories/upload/upload.entity.ts b/libs/dal/src/repositories/upload/upload.entity.ts index 6e89a16f3..d9572da9f 100644 --- a/libs/dal/src/repositories/upload/upload.entity.ts +++ b/libs/dal/src/repositories/upload/upload.entity.ts @@ -26,4 +26,6 @@ export class UploadEntity { status: string; extra: string; + + processInvalidRecords: boolean; } diff --git a/libs/dal/src/repositories/upload/upload.schema.ts b/libs/dal/src/repositories/upload/upload.schema.ts index 2107dfcc1..a7ef10245 100644 --- a/libs/dal/src/repositories/upload/upload.schema.ts +++ b/libs/dal/src/repositories/upload/upload.schema.ts @@ -38,6 +38,10 @@ const uploadSchema = new Schema( authHeaderValue: String, status: String, extra: String, + processInvalidRecords: { + type: Boolean, + default: false, + }, }, { ...schemaOptions } );