From ef27fd611c92cb9fc92905ab1855f4702250a943 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Thu, 11 Jul 2024 15:07:33 +0530 Subject: [PATCH 1/2] feat: basic message Signed-off-by: tipusinghaw --- .../src/agent-service.controller.ts | 5 ++ .../src/agent-service.service.ts | 16 +++- .../src/interface/agent-service.interface.ts | 4 + .../src/connection/connection.controller.ts | 27 +++++- .../src/connection/connection.service.ts | 12 ++- .../connection/dtos/question-answer.dto.ts | 11 +++ apps/connection/src/connection.controller.ts | 5 ++ apps/connection/src/connection.service.ts | 88 ++++++++++++++++++- .../interfaces/question-answer.interfaces.ts | 7 ++ libs/common/src/common.constant.ts | 2 + 10 files changed, 173 insertions(+), 4 deletions(-) diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 7b7b65d21..90a7a57df 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -251,5 +251,10 @@ export class AgentServiceController { async createConnectionInvitation(payload: { url: string; orgId: string; connectionPayload: ICreateConnectionInvitation }): Promise { return this.agentServiceService.createConnectionInvitation(payload.url, payload.orgId, payload.connectionPayload); } + + @MessagePattern({ cmd: 'agent-send-basic-message' }) + async sendBasicMessage(payload: { url, orgId, content }): Promise { + return this.agentServiceService.sendBasicMessage(payload.content, payload.url, payload.orgId); + } } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 3ba236994..adf8752da 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -47,7 +47,8 @@ import { IWallet, ITenantRecord, LedgerListResponse, - ICreateConnectionInvitation + ICreateConnectionInvitation, + IBasicMessage } from './interface/agent-service.interface'; import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; @@ -1673,4 +1674,17 @@ export class AgentServiceService { throw error; } } + + async sendBasicMessage(questionPayload: IBasicMessage, url: string, orgId: string): Promise { + try { + const getApiKey = await this.getOrgAgentApiKey(orgId); + const sendQuestionRes = await this.commonService + .httpPost(url, questionPayload, { headers: { authorization: getApiKey } }) + .then(async (response) => response); + return sendQuestionRes; + } catch (error) { + this.logger.error(`Error in sendBasicMessage in agent service : ${JSON.stringify(error)}`); + throw error; + } + } } diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 850b3c16f..dd25124df 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -570,3 +570,7 @@ export interface ICreateConnectionInvitation { appendedAttachments?: object[]; orgId?: string; } + +export interface IBasicMessage { + content: string; +} diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index 562d2751c..4ea94054a 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -20,7 +20,7 @@ import { ApiResponseDto } from '../dtos/apiResponse.dto'; import { IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; import { SortFields } from 'apps/connection/src/enum/connection.enum'; import { ClientProxy} from '@nestjs/microservices'; -import { QuestionAnswerWebhookDto, QuestionDto} from './dtos/question-answer.dto'; +import { BasicMessageDto, QuestionAnswerWebhookDto, QuestionDto} from './dtos/question-answer.dto'; @UseFilters(CustomExceptionFilter) @Controller() @@ -332,4 +332,29 @@ export class ConnectionController { } return res.status(HttpStatus.CREATED).json(finalResponse); } + + @Post('/orgs/:orgId/basic-message/:connectionId') + @ApiOperation({ summary: '', description: 'send question' }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) + async sendBasicMessage( + @Param('orgId') orgId: string, + @Param('connectionId') connectionId: string, + @Body() basicMessageDto: BasicMessageDto, + @User() reqUser: IUserRequestInterface, + @Res() res: Response + ): Promise { + + basicMessageDto.orgId = orgId; + basicMessageDto.connectionId = connectionId; + const basicMesgResponse = await this.connectionService.sendBasicMessage(basicMessageDto); + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.connection.success.questionSend, // TODO + data: basicMesgResponse + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + + } } diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index b8566168d..23562ae44 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -6,7 +6,7 @@ import { ConnectionDto, CreateOutOfBandConnectionInvitation, ReceiveInvitationDt import { IReceiveInvitationRes, IUserRequestInterface } from './interfaces'; import { IConnectionList } from '@credebl/common/interfaces/connection.interface'; import { AgentConnectionSearchCriteria, IConnectionDetailsById, IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; -import { QuestionDto } from './dtos/question-answer.dto'; +import { BasicMessageDto, QuestionDto } from './dtos/question-answer.dto'; @Injectable() export class ConnectionService extends BaseService { @@ -130,4 +130,14 @@ export class ConnectionService extends BaseService { const payload = { user, createOutOfBandConnectionInvitation }; return this.sendNatsMessage(this.connectionServiceProxy, 'create-connection-invitation', payload); } + + sendBasicMessage( + basicMessageDto: BasicMessageDto + ): Promise { + try { + return this.sendNatsMessage(this.connectionServiceProxy, 'send-basic-message', basicMessageDto); + } catch (error) { + throw new RpcException(error.response); + } + } } diff --git a/apps/api-gateway/src/connection/dtos/question-answer.dto.ts b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts index 583448183..dd701708f 100644 --- a/apps/api-gateway/src/connection/dtos/question-answer.dto.ts +++ b/apps/api-gateway/src/connection/dtos/question-answer.dto.ts @@ -91,4 +91,15 @@ export class QuestionAnswerWebhookDto { @IsOptional() type: string; +} + +export class BasicMessageDto { + @ApiPropertyOptional() + @IsOptional() + @IsString({ message: 'content must be a string' }) + @IsNotEmpty({ message: 'please provide valid content' }) + content: string; + + orgId: string; + connectionId: string; } \ No newline at end of file diff --git a/apps/connection/src/connection.controller.ts b/apps/connection/src/connection.controller.ts index 480fe174b..6e0db6f90 100644 --- a/apps/connection/src/connection.controller.ts +++ b/apps/connection/src/connection.controller.ts @@ -89,4 +89,9 @@ export class ConnectionController { async createConnectionInvitation(payload: ICreateOutOfbandConnectionInvitation): Promise { return this.connectionService.createConnectionInvitation(payload); } + + @MessagePattern({ cmd: 'send-basic-message' }) + async sendBasicMessage(payload: {content: string, orgId: string, connectionId: string}): Promise { + return this.connectionService.sendBasicMesage(payload); + } } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 5853545c2..b0ce3f553 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -23,7 +23,7 @@ import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; -import { IQuestionPayload } from './interfaces/question-answer.interfaces'; +import { IBasicMessage, IQuestionPayload } from './interfaces/question-answer.interfaces'; import { agent_invitations } from '@prisma/client'; @Injectable() @@ -781,4 +781,90 @@ export class ConnectionService { throw new RpcException(error.response ? error.response : error); } } + + async sendBasicMesage(payload: IBasicMessage): Promise { + const { content, orgId, connectionId } = payload; + try { + const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); + + const { agentEndPoint } = agentDetails; + + if (!agentDetails) { + throw new NotFoundException(ResponseMessages.connection.error.agentEndPointNotFound); + } + + const questionPayload = { + content + }; + + const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); + const label = 'send-basic-message'; + const url = await this.sendBasicMessageAgentUrl( + label, + orgAgentType, + agentEndPoint, + agentDetails?.tenantId, + connectionId + ); + + const sendBasicMessage = await this._sendBasicMessage(questionPayload, url, orgId); + return sendBasicMessage; + } catch (error) { + this.logger.error(`[sendQuestion] - error in sending question: ${error}`); + if (error && error?.status && error?.status?.message && error?.status?.message?.error) { + throw new RpcException({ + message: error?.status?.message?.error?.reason + ? error?.status?.message?.error?.reason + : error?.status?.message?.error, + statusCode: error?.status?.code + }); + } else { + throw new RpcException(error.response ? error.response : error); + } + } + } + + async _sendBasicMessage(content: IBasicMessage, url: string, orgId: string): Promise { + const pattern = { cmd: 'agent-send-basic-message' }; + const payload = { content, url, orgId }; + // eslint-disable-next-line no-return-await + return await this.natsCall(pattern, payload); + } + + async sendBasicMessageAgentUrl( + label: string, + orgAgentType: string, + agentEndPoint: string, + tenantId?: string, + connectionId?: string + ): Promise { + try { + let url; + switch (label) { + case 'send-basic-message': { + url = + orgAgentType === OrgAgentType.DEDICATED + ? `${agentEndPoint}${CommonConstants.URL_SEND_BASIC_MESSAGE}`.replace('#', connectionId) + : orgAgentType === OrgAgentType.SHARED + ? `${agentEndPoint}${CommonConstants.URL_SHARED_SEND_BASIC_MESSAGE}` + .replace('#', connectionId) + .replace('@', tenantId) + : null; + break; + } + + default: { + break; + } + } + + if (!url) { + throw new NotFoundException(ResponseMessages.issuance.error.agentUrlNotFound); + } + return url; + } catch (error) { + this.logger.error(`Error in getting basic-message Url: ${JSON.stringify(error)}`); + throw error; + } + } } diff --git a/apps/connection/src/interfaces/question-answer.interfaces.ts b/apps/connection/src/interfaces/question-answer.interfaces.ts index 6fcabd9ef..5b45a9c9e 100644 --- a/apps/connection/src/interfaces/question-answer.interfaces.ts +++ b/apps/connection/src/interfaces/question-answer.interfaces.ts @@ -9,3 +9,10 @@ export interface IValidResponses { connectionId?: string; tenantId?: string; } + + export interface IBasicMessage { + content: string; + orgId?: string; + connectionId?: string; + tenantId?: string; + } \ No newline at end of file diff --git a/libs/common/src/common.constant.ts b/libs/common/src/common.constant.ts index ebb8f4171..a32bbfab0 100644 --- a/libs/common/src/common.constant.ts +++ b/libs/common/src/common.constant.ts @@ -76,6 +76,7 @@ export enum CommonConstants { URL_ACCEPT_CREDENTIALS = '/credentials/accept-offer', URL_SEND_QUESTION = '/question-answer/question/#', URL_QUESTION_ANSWER_RECORD = '/question-answer', + URL_SEND_BASIC_MESSAGE = '/basic-messages/#', // SCHEMA & CRED DEF SERVICES URL_SCHM_CREATE_SCHEMA = '/schemas', @@ -117,6 +118,7 @@ export enum CommonConstants { URL_SHAGENT_SEND_QUESTION = '/multi-tenancy/question-answer/question/#/@', URL_SHAGENT_SEND_ANSWER = '/multi-tenancy/question-answer/answer/#/@', URL_SHAGENT_QUESTION_ANSWER_RECORD = '/multi-tenancy/question-answer/#', + URL_SHARED_SEND_BASIC_MESSAGE = '/multi-tenancy/basic-message/#/@', // PROOF SERVICES URL_SEND_PROOF_REQUEST = '/proofs/request-proof', From a25b5206f92edb3dbe45c74a3ef3a3cc946302d4 Mon Sep 17 00:00:00 2001 From: tipusinghaw Date: Thu, 11 Jul 2024 15:32:22 +0530 Subject: [PATCH 2/2] fix: error log message Signed-off-by: tipusinghaw --- apps/api-gateway/src/connection/connection.controller.ts | 4 ++-- apps/connection/src/connection.service.ts | 2 +- libs/common/src/response-messages/index.ts | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index 4ea94054a..90b535b10 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -334,7 +334,7 @@ export class ConnectionController { } @Post('/orgs/:orgId/basic-message/:connectionId') - @ApiOperation({ summary: '', description: 'send question' }) + @ApiOperation({ summary: '', description: 'Send basic message to connection' }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER, OrgRoles.HOLDER, OrgRoles.SUPER_ADMIN, OrgRoles.PLATFORM_ADMIN) @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) @@ -351,7 +351,7 @@ export class ConnectionController { const basicMesgResponse = await this.connectionService.sendBasicMessage(basicMessageDto); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, - message: ResponseMessages.connection.success.questionSend, // TODO + message: ResponseMessages.connection.success.basicMessage, // TODO data: basicMesgResponse }; return res.status(HttpStatus.CREATED).json(finalResponse); diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index b0ce3f553..f7af58bd4 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -810,7 +810,7 @@ export class ConnectionService { const sendBasicMessage = await this._sendBasicMessage(questionPayload, url, orgId); return sendBasicMessage; } catch (error) { - this.logger.error(`[sendQuestion] - error in sending question: ${error}`); + this.logger.error(`[sendBasicMesage] - error in sending basic message: ${error}`); if (error && error?.status && error?.status?.message && error?.status?.message?.error) { throw new RpcException({ message: error?.status?.message?.error?.reason diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 5a5d393c1..3f581ad97 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -246,7 +246,8 @@ export const ResponseMessages = { fetchConnection: 'Connection details fetched successfully', fetch: 'Connections details fetched successfully', questionAnswerRecord: 'Question Answer record fetched successfully', - questionSend:'Question sent successfully' + questionSend:'Question sent successfully', + basicMessage:'Basic message sent successfully' }, error: { exists: 'Connection is already exist',