From a95fb4cd2a470b0b3b12e846ba8573955eb1ddba Mon Sep 17 00:00:00 2001 From: Krishna Date: Mon, 26 Feb 2024 20:53:12 +0530 Subject: [PATCH] Added support for shortening URL while creating connection Signed-off-by: Krishna Signed-off-by: KulkarniShashank --- .../src/utilities/dtos/store-object.dto.ts | 230 +++++++++--------- apps/connection/src/connection.service.ts | 155 ++++++------ .../src/interfaces/connection.interfaces.ts | 23 ++ .../interfaces/shortening-url.interface.ts | 31 ++- apps/utility/src/utilities.controller.ts | 14 +- apps/utility/src/utilities.service.ts | 3 +- libs/aws/src/aws.service.ts | 2 +- 7 files changed, 251 insertions(+), 207 deletions(-) diff --git a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts index d849702ea..2c497d9f4 100644 --- a/apps/api-gateway/src/utilities/dtos/store-object.dto.ts +++ b/apps/api-gateway/src/utilities/dtos/store-object.dto.ts @@ -4,51 +4,59 @@ import { IsNotEmpty, IsOptional, IsString, IsUrl, ValidateNested } from 'class-v // export type StoreObjectDto = InvitationDto; -class ServiceDto { - @ApiProperty({ - example: 'service-id' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid id' }) - id: string; - - @ApiProperty({ - example: 'http://example.com' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) - @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) - serviceEndpoint: string; +// class ServiceDto { +// @ApiProperty({ +// example: 'service-id' +// }) +// @IsString() +// @IsNotEmpty({ message: 'please provide valid id' }) +// id: string; + +// @ApiProperty({ +// example: 'http://example.com' +// }) +// @IsString() +// @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) +// @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) +// serviceEndpoint: string; + +// @ApiProperty({ +// example: 'service-type' +// }) +// @IsString() +// @IsNotEmpty({ message: 'please provide valid type' }) +// type: string; + +// @ApiProperty({ +// example: ['key1', 'key2'] +// }) +// @IsString({ each: true }) +// recipientKeys: string[]; + +// @ApiPropertyOptional({ +// example: ['key1', 'key2'] +// }) +// @IsOptional() +// @IsString({ each: true }) +// routingKeys: string[]; + +// @ApiPropertyOptional({ +// example: ['true'] +// }) +// @IsOptional() +// @IsString({ each: true }) +// accept: string[]; +// } + +export class LegacyInvitationDto { @ApiProperty({ - example: 'service-type' + example: 'your-type' }) @IsString() - @IsNotEmpty({ message: 'please provide valid type' }) - type: string; - - @ApiProperty({ - example: ['key1', 'key2'] - }) - @IsString({ each: true }) - recipientKeys: string[]; - - @ApiPropertyOptional({ - example: ['key1', 'key2'] - }) - @IsOptional() - @IsString({ each: true }) - routingKeys: string[]; - - @ApiPropertyOptional({ - example: ['true'] - }) - @IsOptional() - @IsString({ each: true }) - accept: string[]; -} + @IsNotEmpty({ message: 'please provide valid @type' }) + '@type': string; -export class InvitationDto { @ApiPropertyOptional({ example: 'your-id' }) @@ -57,13 +65,6 @@ export class InvitationDto { @IsNotEmpty({ message: 'please provide valid @id' }) '@id': string; - @ApiProperty({ - example: 'your-type' - }) - @IsString() - @IsNotEmpty({ message: 'please provide valid @type' }) - '@type': string; - @ApiProperty({ example: 'your-label' }) @@ -72,90 +73,99 @@ export class InvitationDto { label: string; @ApiPropertyOptional({ - example: 'your-goal-code' + example: 'http://example.com/image.jpg' }) - @IsOptional() @IsString() - @IsNotEmpty({ message: 'please provide valid goalCode' }) - goalCode: string; - - @ApiPropertyOptional({ - example: 'your-goal' - }) @IsOptional() + @IsNotEmpty({ message: 'please provide valid imageUrl' }) @IsString() - @IsNotEmpty({ message: 'please provide valid goal' }) - goal: string; + imageUrl?: string; - @ApiPropertyOptional({ - example: ['accept1', 'accept2'] + @ApiProperty({ + example: ['key1', 'key2'] }) - @IsOptional() @IsString({ each: true }) - accept: string[]; + recipientKeys: string[]; - @ApiPropertyOptional({ - example: ['protocol1', 'protocol2'] + @ApiProperty({ + example: 'http://example.com' }) - @IsOptional() - @IsString({ each: true }) - // eslint-disable-next-line camelcase - handshake_protocols: string[]; - - @ApiProperty( - // { - // 'example': [ - // { - // id: 'service-id', - // serviceEndpoint: 'http://example.com', - // type: 'service-type', - // recipientKeys: ['key1', 'key2'], - // routingKeys: ['key1', 'key2'], - // accept: ['true'] - // } - // ] - // } - ) - @ValidateNested({ each: true }) - @Type(() => ServiceDto) - services: ServiceDto[]; + @IsString() + @IsNotEmpty({ message: 'please provide valid serviceEndpoint' }) + @IsUrl({}, { message: 'Invalid serviceEndpoint format' }) + serviceEndpoint: string; @ApiPropertyOptional({ - example: 'http://example.com/image.jpg' + example: ['key1', 'key2'] }) - @IsString() @IsOptional() - @IsNotEmpty({ message: 'please provide valid imageUrl' }) - @IsString() - imageUrl?: string; + @IsString({ each: true }) + routingKeys: string[]; } +// @ApiPropertyOptional({ +// example: 'your-goal-code' +// }) +// @IsOptional() +// @IsString() +// @IsNotEmpty({ message: 'please provide valid goalCode' }) +// goalCode: string; + +// @ApiPropertyOptional({ +// example: 'your-goal' +// }) +// @IsOptional() +// @IsString() +// @IsNotEmpty({ message: 'please provide valid goal' }) +// goal: string; + +// @ApiPropertyOptional({ +// example: ['accept1', 'accept2'] +// }) +// @IsOptional() +// @IsString({ each: true }) +// accept: string[]; + +// @ApiPropertyOptional({ +// example: ['protocol1', 'protocol2'] +// }) +// @IsOptional() +// @IsString({ each: true }) +// // eslint-disable-next-line camelcase +// handshake_protocols: string[]; + +// @ApiProperty( +// // { +// // 'example': [ +// // { +// // id: 'service-id', +// // serviceEndpoint: 'http://example.com', +// // type: 'service-type', +// // recipientKeys: ['key1', 'key2'], +// // routingKeys: ['key1', 'key2'], +// // accept: ['true'] +// // } +// // ] +// // } +// ) +// @ValidateNested({ each: true }) +// @Type(() => ServiceDto) +// services: ServiceDto[]; +// } + export class StoreObjectDto { @ApiProperty({ 'example': { - '@id': 'your-id', '@type': 'your-type', + '@id': 'your-id', label: 'your-label', - goalCode: 'your-goal-code', - goal: 'your-goal', - accept: ['accept1', 'accept2'], - // eslint-disable-next-line camelcase - handshake_protocols: ['protocol1', 'protocol2'], - services: [ - { - id: 'service-id', - serviceEndpoint: 'http://example.com', - type: 'service-type', - recipientKeys: ['key1', 'key2'], - routingKeys: ['key1', 'key2'], - accept: ['true'] - } - // Add more service objects as needed - ], - imageUrl: 'http://example.com/image.jpg' + imageUrl: 'http://example.com/image.jpg', + recipientKeys: ['key1', 'key2'], + serviceEndpoint: 'http://example.com', + routingKeys: ['key1', 'key2'] } }) @ValidateNested() - @Type(() => InvitationDto) - data: InvitationDto; + @Type(() => LegacyInvitationDto) + data: LegacyInvitationDto; } diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 74c0b5a26..d7f0a0510 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -29,7 +29,7 @@ export class ConnectionService { private readonly connectionRepository: ConnectionRepository, private readonly logger: Logger, @Inject(CACHE_MANAGER) private cacheService: Cache - ) { } + ) {} /** * Create connection legacy invitation URL @@ -38,10 +38,18 @@ export class ConnectionService { * @returns Connection legacy invitation URL */ async createLegacyConnectionInvitation(payload: IConnection): Promise { - - const { orgId, multiUseInvitation, autoAcceptConnection, alias, imageUrl, goal, goalCode, handshake, handshakeProtocols } = payload; + const { + orgId, + multiUseInvitation, + autoAcceptConnection, + alias, + imageUrl, + goal, + goalCode, + handshake, + handshakeProtocols + } = payload; try { - const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const { agentEndPoint, id, organisation } = agentDetails; @@ -52,8 +60,8 @@ export class ConnectionService { this.logger.log(`logoUrl:::, ${organisation.logoUrl}`); const connectionPayload = { - multiUseInvitation: multiUseInvitation || true, - autoAcceptConnection: autoAcceptConnection || true, + multiUseInvitation: multiUseInvitation ?? true, + autoAcceptConnection: autoAcceptConnection ?? true, alias: alias || undefined, imageUrl: organisation.logoUrl || imageUrl || undefined, label: organisation.name, @@ -69,22 +77,12 @@ export class ConnectionService { const apiKey = await this._getOrgAgentApiKey(orgId); const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, apiKey); - const invitationObject = createConnectionInvitation?.message?.invitation['@id']; - // Need to implement shortening invitation logic - // Krishna start - // const connectionInvitaion = createConnectionInvitation?.message?.invitation; - // make call to function that will call - // const shortenedUrl = await this.storeObjectAndReturnUrl(connectionInvitaion, multiUseInvitation); - // Krishna end - // eslint-disable-next-line no-console - console.log("This is Invitation object::::::", createConnectionInvitation?.message?.invitation); - let shortenedUrl; - - if (agentDetails?.tenantId) { - shortenedUrl = `${agentEndPoint}/multi-tenancy/url/${agentDetails?.tenantId}/${invitationObject}`; - } else { - shortenedUrl = `${agentEndPoint}/url/${invitationObject}`; - } + const connectionInvitaion = createConnectionInvitation?.message?.invitation; + const shortenedUrl = await this.storeObjectAndReturnUrl( + connectionInvitaion, + connectionPayload.multiUseInvitation + ); + Logger.verbose('This is Invitation object::::::', createConnectionInvitation?.message?.invitation); const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( shortenedUrl, @@ -132,7 +130,6 @@ export class ConnectionService { url: string, apiKey: string ): Promise { - //nats call in agent-service to create an invitation url const pattern = { cmd: 'agent-create-connection-legacy-invitation' }; const payload = { connectionPayload, url, apiKey }; @@ -208,10 +205,7 @@ export class ConnectionService { }; return connectionResponse; } catch (error) { - - this.logger.error( - `[getConnections] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}` - ); + this.logger.error(`[getConnections] [NATS call]- error in fetch connections details : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } @@ -274,15 +268,13 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } - - let apiKey:string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); - if (!apiKey || null === apiKey || undefined === apiKey) { - apiKey = await this._getOrgAgentApiKey(orgId); + // const apiKey = await this._getOrgAgentApiKey(orgId); + let apiKey: string = await this.cacheService.get(CommonConstants.CACHE_APIKEY_KEY); + if (!apiKey || null === apiKey || undefined === apiKey) { + apiKey = await this._getOrgAgentApiKey(orgId); } const createConnectionInvitation = await this._getConnectionsByConnectionId(url, apiKey); return createConnectionInvitation; - - } catch (error) { this.logger.error(`[getConnectionsById] - error in get connections : ${JSON.stringify(error)}`); @@ -298,18 +290,14 @@ export class ConnectionService { } } - async _getConnectionsByConnectionId( - url: string, - apiKey: string - ): Promise { - + async _getConnectionsByConnectionId(url: string, apiKey: string): Promise { //nats call in agent service for fetch connection details const pattern = { cmd: 'agent-get-connection-details-by-connectionId' }; const payload = { url, apiKey }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { + .catch((error) => { this.logger.error( `[_getConnectionsByConnectionId] [NATS call]- error in fetch connections : ${JSON.stringify(error)}` ); @@ -318,7 +306,9 @@ export class ConnectionService { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); } @@ -354,14 +344,21 @@ export class ConnectionService { return message; } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); - throw new HttpException({ - status: error.status, - error: error.message - }, error.status); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); } } - async receiveInvitationUrl(user: IUserRequest, receiveInvitationUrl: IReceiveInvitationUrl, orgId: string): Promise { + async receiveInvitationUrl( + user: IUserRequest, + receiveInvitationUrl: IReceiveInvitationUrl, + orgId: string + ): Promise { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -371,13 +368,14 @@ export class ConnectionService { throw new NotFoundException(ResponseMessages.issuance.error.agentEndPointNotFound); } - let url; if (orgAgentType === OrgAgentType.DEDICATED) { url = `${agentEndPoint}${CommonConstants.URL_RECEIVE_INVITATION_URL}`; } else if (orgAgentType === OrgAgentType.SHARED) { - url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION_URL}` - .replace('#', agentDetails.tenantId); + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION_URL}`.replace( + '#', + agentDetails.tenantId + ); } else { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } @@ -388,8 +386,6 @@ export class ConnectionService { } const createConnectionInvitation = await this._receiveInvitationUrl(url, apiKey, receiveInvitationUrl); return createConnectionInvitation; - - } catch (error) { this.logger.error(`[receiveInvitationUrl] - error in receive invitation url : ${JSON.stringify(error)}`); @@ -410,13 +406,12 @@ export class ConnectionService { apiKey: string, receiveInvitationUrl: IReceiveInvitationUrl ): Promise { - const pattern = { cmd: 'agent-receive-invitation-url' }; const payload = { url, apiKey, receiveInvitationUrl }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { + .catch((error) => { this.logger.error( `[_receiveInvitationUrl] [NATS call]- error in receive invitation url : ${JSON.stringify(error)}` ); @@ -425,11 +420,17 @@ export class ConnectionService { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); } - async receiveInvitation(user: IUserRequest, receiveInvitation: IReceiveInvitation, orgId: string): Promise { + async receiveInvitation( + user: IUserRequest, + receiveInvitation: IReceiveInvitation, + orgId: string + ): Promise { try { const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); @@ -443,8 +444,7 @@ export class ConnectionService { if (orgAgentType === OrgAgentType.DEDICATED) { url = `${agentEndPoint}${CommonConstants.URL_RECEIVE_INVITATION}`; } else if (orgAgentType === OrgAgentType.SHARED) { - url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION}` - .replace('#', agentDetails.tenantId); + url = `${agentEndPoint}${CommonConstants.URL_SHAGENT_RECEIVE_INVITATION}`.replace('#', agentDetails.tenantId); } else { throw new NotFoundException(ResponseMessages.connection.error.agentUrlNotFound); } @@ -455,8 +455,6 @@ export class ConnectionService { } const createConnectionInvitation = await this._receiveInvitation(url, apiKey, receiveInvitation); return createConnectionInvitation; - - } catch (error) { this.logger.error(`[receiveInvitation] - error in receive invitation : ${JSON.stringify(error)}`); @@ -477,37 +475,52 @@ export class ConnectionService { apiKey: string, receiveInvitation: IReceiveInvitation ): Promise { - const pattern = { cmd: 'agent-receive-invitation' }; const payload = { url, apiKey, receiveInvitation }; return this.connectionServiceProxy .send(pattern, payload) .toPromise() - .catch(error => { - this.logger.error( - `[_receiveInvitation] [NATS call]- error in receive invitation : ${JSON.stringify(error)}` - ); + .catch((error) => { + this.logger.error(`[_receiveInvitation] [NATS call]- error in receive invitation : ${JSON.stringify(error)}`); throw new HttpException( { status: error.statusCode, error: error.error?.message?.error ? error.error?.message?.error : error.error, message: error.message - }, error.error); + }, + error.error + ); }); } - // Krishna Start - async storeObjectAndReturnUrl(urlObjectReference: unknown, multiUseInvitation: boolean): Promise { - const persistent:boolean = multiUseInvitation; - const connectionInvitation: unknown = urlObjectReference; + async storeObjectAndReturnUrl(connectionInvitation, persistent: boolean): Promise { + const utilityRequestBodyString = JSON.stringify({ data: connectionInvitation }); + const storeObj = JSON.parse(utilityRequestBodyString); //nats call in agent-service to create an invitation url const pattern = { cmd: 'store-object-return-url' }; - const payload = { persistent, connectionInvitation}; + const payload = { persistent, storeObj }; try { + const message = await this.connectionServiceProxy // eslint-disable-next-line @typescript-eslint/no-explicit-any - const message = await this.connectionServiceProxy.send(pattern, payload).toPromise(); + .send(pattern, payload) + .toPromise() + .catch((error) => { + this.logger.error( + `[storeObjectAndReturnUrl] [NATS call]- error in storing object and returning url : ${JSON.stringify( + error + )}` + ); + throw new HttpException( + { + status: error.statusCode, + error: error.error?.message?.error ? error.error?.message?.error : error.error, + message: error.message + }, + error.error + ); + }); return message; } catch (error) { this.logger.error(`catch: ${JSON.stringify(error)}`); @@ -520,6 +533,4 @@ export class ConnectionService { ); } } - // Krishna End } - diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index f26a6fd2f..0a4d53cb2 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -99,6 +99,29 @@ interface IInvitation { invitation: string; } + +interface IService { + id: string; + serviceEndpoint: string; + type: string; + recipientKeys: string[]; + routingKeys: string[]; + accept: string[]; +} + +export interface ILegacyInvitation { + '@id': string; + '@type': string; + label: string; + goalCode: string; + goal: string; + accept: string[]; + // eslint-disable-next-line camelcase + handshake_protocols: string[]; + services: IService[]; + imageUrl?: string; +} + export interface OrgAgent { organisation: organisation; id: string; diff --git a/apps/utility/interfaces/shortening-url.interface.ts b/apps/utility/interfaces/shortening-url.interface.ts index 755649e40..d8bdba0de 100644 --- a/apps/utility/interfaces/shortening-url.interface.ts +++ b/apps/utility/interfaces/shortening-url.interface.ts @@ -18,28 +18,25 @@ export interface IUtilities { } // export type StoreObjectDto = InvitationDto; -interface ServiceDto { - id: string; - serviceEndpoint: string; - type: string; - recipientKeys: string[]; - routingKeys: string[]; - accept: string[]; -} +// interface IService { +// id: string; +// serviceEndpoint: string; +// type: string; +// recipientKeys: string[]; +// routingKeys: string[]; +// accept: string[]; +// } -export interface IInvitation { - '@id': string; +export interface ILegacyInvitation { '@type': string; + '@id': string; label: string; - goalCode: string; - goal: string; - accept: string[]; - // eslint-disable-next-line camelcase - handshake_protocols: string[]; - services: ServiceDto[]; imageUrl?: string; + recipientKeys: string[]; + serviceEndpoint: string; + routingKeys: string[] } export interface IStoreObject { - data: IInvitation; + data: ILegacyInvitation; } \ No newline at end of file diff --git a/apps/utility/src/utilities.controller.ts b/apps/utility/src/utilities.controller.ts index d5481dfdd..c1fd1fd0c 100644 --- a/apps/utility/src/utilities.controller.ts +++ b/apps/utility/src/utilities.controller.ts @@ -1,4 +1,4 @@ -import { Controller } from '@nestjs/common'; +import { Controller, Logger } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { UtilitiesService } from './utilities.service'; import { IShorteningUrlData, IStoreObject } from '../interfaces/shortening-url.interface'; @@ -19,11 +19,13 @@ export class UtilitiesController { @MessagePattern({ cmd: 'store-object-return-url' }) async storeObject(payload: {persistent: boolean, storeObj: IStoreObject}): Promise { - // eslint-disable-next-line no-console - console.log('Reached in Utility microservice controller. The object to store is::::::: ', JSON.stringify(payload.storeObj)); + try { const url:string = await this.utilitiesService.storeObject(payload); - // eslint-disable-next-line no-console - console.log('Received `url` in Utility microservice controller:::::::', url); - return url; + return url; + } catch (error) { + Logger.error(error); + throw new Error('Error occured in Utility Microservices Controller'); + } + } } \ No newline at end of file diff --git a/apps/utility/src/utilities.service.ts b/apps/utility/src/utilities.service.ts index 2994a1bff..10e3db107 100644 --- a/apps/utility/src/utilities.service.ts +++ b/apps/utility/src/utilities.service.ts @@ -60,7 +60,8 @@ export class UtilitiesService { return url; // return 'success'; } catch (error) { - throw new Error('An error occurred while uploading data to S3.'); + Logger.error(error); + throw new Error('An error occurred while uploading data to S3. Error::::::'); } } } diff --git a/libs/aws/src/aws.service.ts b/libs/aws/src/aws.service.ts index 9c14ab513..9573d07db 100644 --- a/libs/aws/src/aws.service.ts +++ b/libs/aws/src/aws.service.ts @@ -95,7 +95,7 @@ export class AwsService { } async storeObject(persistent: boolean, key: number, body: unknown): Promise { - const objKey: string = persistent ? `persistent/${key}` : `default/${key}`; + const objKey: string = persistent.valueOf() ? `persistent/${key}` : `default/${key}`; const buf = Buffer.from(JSON.stringify(body)); const params: AWS.S3.PutObjectRequest = { Bucket: process.env.AWS_S3_STOREOBJECT_BUCKET,