From 0b081742f919c3d8843831cdc542fd0c4f5e0a59 Mon Sep 17 00:00:00 2001 From: Nishad Date: Wed, 4 Oct 2023 20:09:48 +0530 Subject: [PATCH] worked on the eslint issues Signed-off-by: Nishad --- .../src/ecosystem/dtos/send-invitation.dto.ts | 28 +++++ .../src/ecosystem/ecosystem.controller.ts | 35 ++++++ .../src/ecosystem/ecosystem.service.ts | 14 +++ apps/ecosystem/dtos/send-invitation.dto.ts | 12 ++ apps/ecosystem/enums/ecosystem.enum.ts | 6 + apps/ecosystem/src/ecosystem.controller.ts | 14 +++ apps/ecosystem/src/ecosystem.repository.ts | 79 ++++++++++++- apps/ecosystem/src/ecosystem.service.ts | 105 +++++++++++++++++- .../templates/EcosystemInviteTemplate.ts | 66 +++++++++++ libs/common/src/response-messages/index.ts | 4 +- 10 files changed, 357 insertions(+), 6 deletions(-) create mode 100644 apps/api-gateway/src/ecosystem/dtos/send-invitation.dto.ts create mode 100644 apps/ecosystem/dtos/send-invitation.dto.ts create mode 100644 apps/ecosystem/templates/EcosystemInviteTemplate.ts diff --git a/apps/api-gateway/src/ecosystem/dtos/send-invitation.dto.ts b/apps/api-gateway/src/ecosystem/dtos/send-invitation.dto.ts new file mode 100644 index 000000000..a9246e7bc --- /dev/null +++ b/apps/api-gateway/src/ecosystem/dtos/send-invitation.dto.ts @@ -0,0 +1,28 @@ +import { ApiExtraModels, ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsEmail, IsNotEmpty, IsString, ValidateNested } from 'class-validator'; +import { Transform, Type } from 'class-transformer'; + +import { trim } from '@credebl/common/cast.helper'; + +@ApiExtraModels() +export class EcosystemInvitationDto { + + @ApiProperty({ example: 'acqx@getnada.com' }) + @IsEmail() + @Transform(({ value }) => trim(value)) + @IsNotEmpty({ message: 'Please provide valid email' }) + @IsString({ message: 'email should be string' }) + email: string; + +} + +@ApiExtraModels() +export class BulkEcosystemInvitationDto { + + @ApiProperty({ type: [EcosystemInvitationDto] }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => EcosystemInvitationDto) + invitations: EcosystemInvitationDto[]; + ecosystemId: string; +} \ No newline at end of file diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index 88658779f..b05b607c1 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -15,6 +15,8 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; import { EditEcosystemDto } from './dtos/edit-ecosystem-dto'; import { AuthGuard } from '@nestjs/passport'; +import { User } from '../authz/decorators/user.decorator'; +import { BulkEcosystemInvitationDto } from './dtos/send-invitation.dto'; @UseFilters(CustomExceptionFilter) @@ -57,6 +59,38 @@ export class EcosystemController { return res.status(HttpStatus.CREATED).json(finalResponse); } + + /** + * + * @param bulkInvitationDto + * @param ecosystemId + * @param user + * @param res + * @returns Ecosystem invitation send details + */ + @Post('/:ecosystemId/invitations') + @ApiOperation({ + summary: 'Send ecosystem invitation', + description: 'Send ecosystem invitation' + }) + @ApiResponse({ status: 201, description: 'Success', type: ApiResponseDto }) + @UseGuards(AuthGuard('jwt')) + @ApiBearerAuth() + async createInvitation(@Body() bulkInvitationDto: BulkEcosystemInvitationDto, @Param('ecosystemId') ecosystemId: string, @User() user: user, @Res() res: Response): Promise { + + bulkInvitationDto.ecosystemId = ecosystemId; + await this.ecosystemService.createInvitation(bulkInvitationDto, String(user.id)); + + const finalResponse: IResponseType = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.ecosystem.success.createInvitation + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + + } + + @Put('/:ecosystemId/') @ApiOperation({ summary: 'Edit ecosystem', description: 'Edit existing ecosystem' }) @ApiResponse({ status: 201, description: 'Success', type: ApiResponseDto }) @@ -70,4 +104,5 @@ export class EcosystemController { }; return res.status(HttpStatus.CREATED).json(finalResponse); } + } \ No newline at end of file diff --git a/apps/api-gateway/src/ecosystem/ecosystem.service.ts b/apps/api-gateway/src/ecosystem/ecosystem.service.ts index 760bc98b8..ff7b3702f 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.service.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.service.ts @@ -2,6 +2,7 @@ import { Inject } from '@nestjs/common'; import { Injectable } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; +import { BulkEcosystemInvitationDto } from './dtos/send-invitation.dto'; @Injectable() @@ -38,5 +39,18 @@ export class EcosystemService extends BaseService { async getAllEcosystem(): Promise<{ response: object }> { return this.sendNats(this.serviceProxy, 'get-all-ecosystem', ''); } + + + /** + * + * @param bulkInvitationDto + * @param userId + * @returns + */ + async createInvitation(bulkInvitationDto: BulkEcosystemInvitationDto, userId: string): Promise { + const payload = { bulkInvitationDto, userId }; + return this.sendNats(this.serviceProxy, 'send-ecosystem-invitation', payload); + } + } diff --git a/apps/ecosystem/dtos/send-invitation.dto.ts b/apps/ecosystem/dtos/send-invitation.dto.ts new file mode 100644 index 000000000..476415ecd --- /dev/null +++ b/apps/ecosystem/dtos/send-invitation.dto.ts @@ -0,0 +1,12 @@ +import { ApiExtraModels } from '@nestjs/swagger'; + +@ApiExtraModels() +export class SendInvitationDto { + email: string; +} + +@ApiExtraModels() +export class BulkSendInvitationDto { + invitations: SendInvitationDto[]; + ecosystemId: string; +} \ No newline at end of file diff --git a/apps/ecosystem/enums/ecosystem.enum.ts b/apps/ecosystem/enums/ecosystem.enum.ts index 6bf735879..13d0022fb 100644 --- a/apps/ecosystem/enums/ecosystem.enum.ts +++ b/apps/ecosystem/enums/ecosystem.enum.ts @@ -6,4 +6,10 @@ export enum EcosystemRoles { export enum EcosystemOrgStatus { ACTIVE = 'ACTIVE' +} + +export enum EcosystemInvitationStatus { + ACCEPTED = 'accepted', + REJECTED = 'rejected', + PENDING = 'pending' } \ No newline at end of file diff --git a/apps/ecosystem/src/ecosystem.controller.ts b/apps/ecosystem/src/ecosystem.controller.ts index ddcb718c0..abbdd3149 100644 --- a/apps/ecosystem/src/ecosystem.controller.ts +++ b/apps/ecosystem/src/ecosystem.controller.ts @@ -3,6 +3,7 @@ import { Controller, Logger } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { EcosystemService } from './ecosystem.service'; import { Body } from '@nestjs/common'; +import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; @Controller() export class EcosystemController { @@ -39,5 +40,18 @@ export class EcosystemController { async getAllEcosystems(): Promise { return this.ecosystemService.getAllEcosystem(); } + + + /** + * + * @param payload + * @returns Sent ecosystem invitations status + */ + @MessagePattern({ cmd: 'send-ecosystem-invitation' }) + async createInvitation( + @Body() payload: { bulkInvitationDto: BulkSendInvitationDto; userId: string } + ): Promise { + return this.ecosystemService.createInvitation(payload.bulkInvitationDto, payload.userId); + } } diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index f7e7187b0..db6c50e0a 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -1,7 +1,8 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'; import { PrismaService } from '@credebl/prisma-service'; -import { ecosystem } from '@prisma/client'; -import {EcosystemOrgStatus, EcosystemRoles} from '../enums/ecosystem.enum'; +// eslint-disable-next-line camelcase +import { ecosystem, ecosystem_invitations } from '@prisma/client'; +import {EcosystemInvitationStatus, EcosystemOrgStatus, EcosystemRoles} from '../enums/ecosystem.enum'; // eslint-disable-next-line camelcase @Injectable() export class EcosystemRepository { @@ -112,5 +113,77 @@ export class EcosystemRepository { } } + /** + * + * @param ecosystemId + * @returns Get specific ecosystem details + */ + async getEcosystemDetails(ecosystemId: string): Promise { + try { + return this.prisma.ecosystem.findFirst({ + where: { + id: ecosystemId + } + }); + } catch (error) { + this.logger.error(`error: ${JSON.stringify(error)}`); + throw new InternalServerErrorException(error); + } + } + + /** + * + * @param queryObject + * @returns Get all ecosystem invitations + */ + async getEcosystemInvitations( + queryObject: object + // eslint-disable-next-line camelcase + ): Promise { + try { + return this.prisma.ecosystem_invitations.findMany({ + where: { + ...queryObject + }, + include: { + ecosystem: true + } + }); + } catch (error) { + this.logger.error(`error: ${JSON.stringify(error)}`); + throw new InternalServerErrorException(error); + } + } + + + /** + * + * @param email + * @param ecosystemId + * @param userId + * @returns + */ + async createSendInvitation( + email: string, + ecosystemId: string, + userId: string + // eslint-disable-next-line camelcase + ): Promise { + try { + return this.prisma.ecosystem_invitations.create({ + data: { + email, + userId, + ecosystem: {connect: {id: ecosystemId}}, + status: EcosystemInvitationStatus.PENDING, + orgId: '' + } + }); + } catch (error) { + this.logger.error(`error: ${JSON.stringify(error)}`); + throw new InternalServerErrorException(error); + } + } + } \ No newline at end of file diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index c10e1ada1..0b24abc4d 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -1,12 +1,21 @@ // eslint-disable-next-line camelcase -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; import { EcosystemRepository } from './ecosystem.repository'; import { ResponseMessages } from '@credebl/common/response-messages'; +import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; +import { RpcException } from '@nestjs/microservices'; +import { PrismaService } from '@credebl/prisma-service'; +import { EcosystemInviteTemplate } from '../templates/EcosystemInviteTemplate'; +import { EmailDto } from '@credebl/common/dtos/email.dto'; +import { sendEmail } from '@credebl/common/send-grid-helper-file'; @Injectable() export class EcosystemService { constructor( - private readonly ecosystemRepository: EcosystemRepository + private readonly ecosystemRepository: EcosystemRepository, + private readonly logger: Logger, + private readonly prisma: PrismaService + ) { } /** @@ -54,4 +63,96 @@ export class EcosystemService { } return getAllEcosystemDetails; } + + + /** + * + * @param bulkInvitationDto + * @param userId + * @returns + */ + async createInvitation(bulkInvitationDto: BulkSendInvitationDto, userId: string): Promise { + const { invitations, ecosystemId } = bulkInvitationDto; + + try { + const ecosystemDetails = await this.ecosystemRepository.getEcosystemDetails(ecosystemId); + + for (const invitation of invitations) { + const { email } = invitation; + + const isInvitationExist = await this.checkInvitationExist(email, ecosystemId); + + if (!isInvitationExist) { + await this.ecosystemRepository.createSendInvitation(email, ecosystemId, userId); + + try { + await this.sendInviteEmailTemplate(email, ecosystemDetails.name); + } catch (error) { + throw new InternalServerErrorException(ResponseMessages.user.error.emailSend); + } + } + } + return ResponseMessages.ecosystem.success.createInvitation; + } catch (error) { + this.logger.error(`In send Invitation : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } + + + /** + * + * @param email + * @param ecosystemId + * @returns Returns boolean status for invitation + */ + async checkInvitationExist( + email: string, + ecosystemId: string + ): Promise { + try { + + const query = { + email, + ecosystemId + }; + + const invitations = await this.ecosystemRepository.getEcosystemInvitations(query); + + if (0 < invitations.length) { + return true; + } + return false; + } catch (error) { + throw new RpcException(error.response ? error.response : error); + } + } + + /** + * + * @param email + * @param ecosystemName + * @returns Send invitation mail + */ + async sendInviteEmailTemplate( + email: string, + ecosystemName: string + ): Promise { + const platformConfigData = await this.prisma.platform_config.findMany(); + + const urlEmailTemplate = new EcosystemInviteTemplate(); + const emailData = new EmailDto(); + emailData.emailFrom = platformConfigData[0].emailFrom; + emailData.emailTo = email; + emailData.emailSubject = `${process.env.PLATFORM_NAME} Platform: Invitation`; + + emailData.emailHtml = await urlEmailTemplate.sendInviteEmailTemplate(email, ecosystemName); + + //Email is sent to user for the verification through emailData + const isEmailSent = await sendEmail(emailData); + + return isEmailSent; + } + + } diff --git a/apps/ecosystem/templates/EcosystemInviteTemplate.ts b/apps/ecosystem/templates/EcosystemInviteTemplate.ts new file mode 100644 index 000000000..2f872654a --- /dev/null +++ b/apps/ecosystem/templates/EcosystemInviteTemplate.ts @@ -0,0 +1,66 @@ +export class EcosystemInviteTemplate { + + public sendInviteEmailTemplate( + email: string, + ecosystemName: string + ): string { + + const validUrl = `${process.env.FRONT_END_URL}/authentication/sign-in`; + + const message = `You have been invited to join the ecosystem so please log in and accept the ecosystem “INVITATION” and participate in the ecosystem`; + const year: number = new Date().getFullYear(); + + return ` + + + + + + + + + +
+ +
+

+ Hello ${email}, +

+

+ Congratulations! + Your have been successfully invited to join. +

    +
  • Ecosystem: ${ecosystemName}
  • +
+ ${message} + + +

In case you need any assistance to access your account, please contact CREDEBL Platform +

+
+ +
+
+ + + `; + + } + + +} \ No newline at end of file diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 23b4708d0..963338135 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -178,7 +178,9 @@ export const ResponseMessages = { success: { create: 'Ecosystem created successfully', update: 'Ecosystem updated successfully', - fetch: 'Ecosystem fetched successfully' + fetch: 'Ecosystem fetched successfully', + createInvitation: 'Ecosystem invitations sent successfully', + }, error: { notCreated: 'Error while creating ecosystem',