From 7f67828a83f98e123830610e3f84ada25d7ff55f Mon Sep 17 00:00:00 2001 From: Andrew Radulescu Date: Wed, 25 Sep 2024 11:34:16 +0300 Subject: [PATCH] feat: [Contracts] Download contract --- .serverless/meta.json | 18 +++++ backend/src/main.ts | 3 + .../services/document-pdf-generator.ts | 2 +- .../create-document-contract.usecase.ts | 1 + ...document-contracts-by-volunteer.usecase.ts | 4 +- .../get-many-document-contracts.usecase.ts | 22 +++++- ...t-one-document-contract-for-ngo.usecase.ts | 11 ++- ...document-contract-for-volunteer.usecase.ts | 11 ++- frontend/src/common/utils/utils.ts | 1 + .../src/components/ContractInfoContent.tsx | 11 ++- .../src/components/DocumentContractsTable.tsx | 11 ++- .../.gitignore | 0 .../README.md | 0 lambda-pdf-generator/handler.js | 77 +++++++++++++++++++ .../package-lock.json | 0 .../package.json | 0 lambda-pdf-generator/serverless.yml | 23 ++++++ mobile/src/screens/DocumentsContract.tsx | 8 +- pdf-generator-puppeteer/handler.js | 73 ------------------ pdf-generator-puppeteer/serverless.yml | 21 ----- 20 files changed, 191 insertions(+), 106 deletions(-) create mode 100644 .serverless/meta.json rename {pdf-generator-puppeteer => lambda-pdf-generator}/.gitignore (100%) rename {pdf-generator-puppeteer => lambda-pdf-generator}/README.md (100%) create mode 100644 lambda-pdf-generator/handler.js rename {pdf-generator-puppeteer => lambda-pdf-generator}/package-lock.json (100%) rename {pdf-generator-puppeteer => lambda-pdf-generator}/package.json (100%) create mode 100644 lambda-pdf-generator/serverless.yml delete mode 100644 pdf-generator-puppeteer/handler.js delete mode 100644 pdf-generator-puppeteer/serverless.yml diff --git a/.serverless/meta.json b/.serverless/meta.json new file mode 100644 index 00000000..1aa148e3 --- /dev/null +++ b/.serverless/meta.json @@ -0,0 +1,18 @@ +{ + "unknown": { + "versionSfCore": null, + "versionFramework": "4.2.4", + "isWithinCompose": false, + "composeOrgName": null, + "composeServiceName": null, + "command": [], + "options": {}, + "error": null, + "machineId": "4fa7584a6810489c980dfeebe3fa579c", + "serviceProviderAwsCfStackId": null, + "serviceProviderAwsCfStackCreated": null, + "serviceProviderAwsCfStackUpdated": null, + "serviceProviderAwsCfStackStatus": null, + "serviceProviderAwsCfStackOutputs": null + } +} \ No newline at end of file diff --git a/backend/src/main.ts b/backend/src/main.ts index 58ebd998..554f142c 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -6,10 +6,13 @@ import { AppModule } from './app.module'; import { Environment } from './infrastructure/config/environment-config'; import { ExceptionsFilter } from './infrastructure/filters/exception.filter'; import { createQueueMonitoring } from './infrastructure/config/create-bull-board'; +import { json } from 'express'; async function bootstrap(): Promise { const app = await NestFactory.create(AppModule); + app.use(json({ limit: '50mb' })); // Increase the limit to 50mb to avoid the error "Request Entity Too Large" + app.enableCors({ exposedHeaders: 'content-disposition', // Allow header in the Axios Response Headers }); diff --git a/backend/src/modules/documents/services/document-pdf-generator.ts b/backend/src/modules/documents/services/document-pdf-generator.ts index 64efce02..9f68143d 100644 --- a/backend/src/modules/documents/services/document-pdf-generator.ts +++ b/backend/src/modules/documents/services/document-pdf-generator.ts @@ -130,7 +130,7 @@ export class DocumentPDFGenerator { // HTMLtoPDF(fileHTML); const result = await axios.post( - 'https://iywe2rp7u1.execute-api.us-east-1.amazonaws.com/test', + 'https://715w11fnq9.execute-api.eu-west-1.amazonaws.com/generate-pdf', fileHTML, { headers: { diff --git a/backend/src/usecases/documents/new_contracts/create-document-contract.usecase.ts b/backend/src/usecases/documents/new_contracts/create-document-contract.usecase.ts index 9035def8..55e0cf32 100644 --- a/backend/src/usecases/documents/new_contracts/create-document-contract.usecase.ts +++ b/backend/src/usecases/documents/new_contracts/create-document-contract.usecase.ts @@ -138,6 +138,7 @@ export class CreateDocumentContractUsecase implements IUseCaseService { // 8. Generate the PDF try { + // TODO: Make it async, so we can return the contract id immediately await this.documentPDFGenerator.generateContractPDF(contract.id); } catch (error) { this.logger.error( diff --git a/backend/src/usecases/documents/new_contracts/get-many-document-contracts-by-volunteer.usecase.ts b/backend/src/usecases/documents/new_contracts/get-many-document-contracts-by-volunteer.usecase.ts index 8a9e18eb..fe5751cb 100644 --- a/backend/src/usecases/documents/new_contracts/get-many-document-contracts-by-volunteer.usecase.ts +++ b/backend/src/usecases/documents/new_contracts/get-many-document-contracts-by-volunteer.usecase.ts @@ -41,10 +41,12 @@ export class GetManyDocumentContractsByVolunteerUsecase } // 2. Find the document contracts based on the volunteerId - return this.documentContractFacade.findMany({ + const contracts = await this.documentContractFacade.findMany({ ...paginationOptions, organizationId, volunteerId: volunteer.id, }); + + return contracts; } } diff --git a/backend/src/usecases/documents/new_contracts/get-many-document-contracts.usecase.ts b/backend/src/usecases/documents/new_contracts/get-many-document-contracts.usecase.ts index 10e3d931..3bf12f64 100644 --- a/backend/src/usecases/documents/new_contracts/get-many-document-contracts.usecase.ts +++ b/backend/src/usecases/documents/new_contracts/get-many-document-contracts.usecase.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { IUseCaseService } from 'src/common/interfaces/use-case-service.interface'; import { Pagination } from 'src/infrastructure/base/repository-with-pagination.class'; +import { S3Service } from 'src/infrastructure/providers/s3/module/s3.service'; import { FindManyDocumentContractListViewOptions, IDocumentContractListViewModel, @@ -13,11 +14,30 @@ export class GetManyDocumentContractsUsecase { constructor( private readonly documentContractFacade: DocumentContractFacade, + private readonly s3Service: S3Service, ) {} public async execute( findOptions: FindManyDocumentContractListViewOptions, ): Promise> { - return this.documentContractFacade.findMany(findOptions); + const contracts = await this.documentContractFacade.findMany(findOptions); + + const contractsWithPath = await Promise.all( + contracts.items.map(async (contract) => { + return { + ...contract, + documentFilePath: contract.documentFilePath + ? await this.s3Service.generatePresignedURL( + contract.documentFilePath, + ) + : null, + }; + }), + ); + + return { + ...contracts, + items: contractsWithPath, + }; } } diff --git a/backend/src/usecases/documents/new_contracts/get-one-document-contract-for-ngo.usecase.ts b/backend/src/usecases/documents/new_contracts/get-one-document-contract-for-ngo.usecase.ts index db256721..6d7c57ae 100644 --- a/backend/src/usecases/documents/new_contracts/get-one-document-contract-for-ngo.usecase.ts +++ b/backend/src/usecases/documents/new_contracts/get-one-document-contract-for-ngo.usecase.ts @@ -1,5 +1,6 @@ import { Injectable } from '@nestjs/common'; import { ExceptionsService } from 'src/infrastructure/exceptions/exceptions.service'; +import { S3Service } from 'src/infrastructure/providers/s3/module/s3.service'; import { ContractExceptionMessages } from 'src/modules/documents/exceptions/contract.exceptions'; import { IDocumentContractWebItemModel } from 'src/modules/documents/models/document-contract-web-item.model'; import { DocumentContractFacade } from 'src/modules/documents/services/document-contract.facade'; @@ -9,6 +10,7 @@ export class GetOneDocumentContractForNgoUsecase { constructor( private readonly documentContractFacade: DocumentContractFacade, private readonly exceptionService: ExceptionsService, + private readonly s3Service: S3Service, ) {} async execute({ @@ -29,6 +31,13 @@ export class GetOneDocumentContractForNgoUsecase { ); } - return contract; + const contractWithPath = { + ...contract, + documentFilePath: contract.documentFilePath + ? await this.s3Service.generatePresignedURL(contract.documentFilePath) + : null, + }; + + return contractWithPath; } } diff --git a/backend/src/usecases/documents/new_contracts/get-one-document-contract-for-volunteer.usecase.ts b/backend/src/usecases/documents/new_contracts/get-one-document-contract-for-volunteer.usecase.ts index c688f4a0..a9485b71 100644 --- a/backend/src/usecases/documents/new_contracts/get-one-document-contract-for-volunteer.usecase.ts +++ b/backend/src/usecases/documents/new_contracts/get-one-document-contract-for-volunteer.usecase.ts @@ -1,5 +1,6 @@ import { Injectable } from '@nestjs/common'; import { ExceptionsService } from 'src/infrastructure/exceptions/exceptions.service'; +import { S3Service } from 'src/infrastructure/providers/s3/module/s3.service'; import { DocumentContractListViewEntity } from 'src/modules/documents/entities/document-contract-list-view.entity'; import { ContractExceptionMessages } from 'src/modules/documents/exceptions/contract.exceptions'; import { DocumentContractFacade } from 'src/modules/documents/services/document-contract.facade'; @@ -12,6 +13,7 @@ export class GetOneDocumentContractForVolunteerUsecase { private readonly documentContractFacade: DocumentContractFacade, private readonly volunteerFacade: VolunteerFacade, private readonly exceptionService: ExceptionsService, + private readonly s3Service: S3Service, ) {} async execute({ @@ -45,6 +47,13 @@ export class GetOneDocumentContractForVolunteerUsecase { ); } - return contract; + const contractWithPath = { + ...contract, + documentFilePath: contract.documentFilePath + ? await this.s3Service.generatePresignedURL(contract.documentFilePath) + : null, + }; + + return contractWithPath; } } diff --git a/frontend/src/common/utils/utils.ts b/frontend/src/common/utils/utils.ts index 60330074..063cb942 100644 --- a/frontend/src/common/utils/utils.ts +++ b/frontend/src/common/utils/utils.ts @@ -86,6 +86,7 @@ export const downloadFile = (uri: string, name: string) => { const link = document.createElement('a'); link.href = uri; link.setAttribute('download', name); + link.setAttribute('target', '_blank'); document.body.appendChild(link); link.click(); link.remove(); diff --git a/frontend/src/components/ContractInfoContent.tsx b/frontend/src/components/ContractInfoContent.tsx index fbfdd4a5..ae3a7071 100644 --- a/frontend/src/components/ContractInfoContent.tsx +++ b/frontend/src/components/ContractInfoContent.tsx @@ -7,6 +7,7 @@ import FormReadOnlyElement from './FormReadOnlyElement'; import { ApprovedDocumentContractStatusMapper, DocumentContractStatusMarkerColorMapper, + downloadFile, formatDate, } from '../common/utils/utils'; import StatusWithMarker from './StatusWithMarker'; @@ -98,8 +99,14 @@ export const ContractInfoContent = ({