diff --git a/apps/agent-provisioning/src/agent-provisioning.service.ts b/apps/agent-provisioning/src/agent-provisioning.service.ts index 2f2cc5b2e..725240084 100644 --- a/apps/agent-provisioning/src/agent-provisioning.service.ts +++ b/apps/agent-provisioning/src/agent-provisioning.service.ts @@ -1,10 +1,13 @@ -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; -import { RpcException } from '@nestjs/microservices'; -import { IWalletProvision } from './interface/agent-provisioning.interfaces'; import * as dotenv from 'dotenv'; -import { AgentType } from '@credebl/enum/enum'; import * as fs from 'fs'; + +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; + +import { AgentType } from '@credebl/enum/enum'; +import { IWalletProvision } from './interface/agent-provisioning.interfaces'; +import { RpcException } from '@nestjs/microservices'; import { exec } from 'child_process'; + dotenv.config(); @Injectable() diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index d368781cd..2a293086b 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -1,32 +1,33 @@ -import { Controller } from '@nestjs/common'; -import { MessagePattern } from '@nestjs/microservices'; -import { AgentServiceService } from './agent-service.service'; import { + IAgentConfigure, + IAgentProofRequest, + IAgentSpinupDto, IAgentStatus, IConnectionDetails, - IUserRequestInterface, - ISendProofRequestPayload, - IAgentSpinUpSatus, + ICreateConnectionInvitation, + IDidCreate, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, - IAgentSpinupDto, IIssuanceCreateOffer, + IOutOfBandCredentialOffer, + ISendProofRequestPayload, + IStoreAgent, + IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, - ITenantSchema, - IOutOfBandCredentialOffer, - IAgentProofRequest, - IDidCreate, - IWallet, ITenantRecord, - ICreateConnectionInvitation, - IStoreAgent, - IAgentConfigure + ITenantSchema, + IUserRequestInterface, + IWallet } from './interface/agent-service.interface'; -import { user } from '@prisma/client'; -import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; + +import { AgentServiceService } from './agent-service.service'; import { AgentSpinUpStatus } from '@credebl/enum/enum'; +import { Controller } from '@nestjs/common'; +import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; +import { MessagePattern } from '@nestjs/microservices'; import { SignDataDto } from '../../api-gateway/src/agent-service/dto/agent-service.dto'; +import { user } from '@prisma/client'; @Controller() export class AgentServiceController { @@ -49,7 +50,7 @@ export class AgentServiceController { async createTenant(payload: { createTenantDto: ITenantDto; user: IUserRequestInterface; - }): Promise { + }): Promise { return this.agentServiceService.createTenant(payload.createTenantDto, payload.user); } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index fb41a1801..524ffe4f8 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -33,7 +33,6 @@ import { ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, - IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, @@ -126,15 +125,15 @@ export class AgentServiceService { }> { let agentProcess: ICreateOrgAgent; try { - await this.processWalletProvision(agentSpinupDto, user); - return { agentSpinupStatus: AgentSpinUpStatus.PROCESSED }; + await this.provisionWallet(agentSpinupDto, user); + return { agentSpinupStatus: AgentSpinUpStatus.DID_CREATED }; } catch (error) { this.handleErrorOnWalletProvision(agentSpinupDto, error, agentProcess); throw new RpcException(error.response ?? error); } } - private async processWalletProvision(agentSpinupDto: IAgentSpinupDto, user: IUserRequestInterface): Promise { + private async provisionWallet(agentSpinupDto: IAgentSpinupDto, user: IUserRequestInterface): Promise { let platformAdminUser; let userId: string; let agentProcess: ICreateOrgAgent; @@ -144,6 +143,7 @@ export class AgentServiceService { this.agentServiceRepository.getPlatformConfigDetails(), this.agentServiceRepository.getAgentTypeDetails(), this.agentServiceRepository.getLedgerDetails( + // TODO: Do we want to get first element from ledgerName agentSpinupDto.ledgerName ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet] ) ]); @@ -182,20 +182,13 @@ export class AgentServiceService { // Get genesis URL and ledger details const ledgerDetails = await this.agentServiceRepository.getGenesisUrl(agentSpinupDto.ledgerId); - if (AgentSpinUpStatus.PROCESSED === getOrgAgent?.agentSpinUpStatus) { - throw new BadRequestException(ResponseMessages.agent.error.walletAlreadyProcessing, { + if (AgentSpinUpStatus.WALLET_CREATED === getOrgAgent?.agentSpinUpStatus) { + throw new BadRequestException(ResponseMessages.agent.error.walletAlreadyCreated, { cause: new Error(), description: ResponseMessages.errorMessages.badRequest }); } - if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { - throw new ConflictException(ResponseMessages.agent.error.walletAlreadyCreated, { - cause: new Error(), - description: ResponseMessages.errorMessages.conflict - }); - } - if (!agentSpinupDto.orgId) { if (platformAdminOrgDetails) { agentSpinupDto.orgId = platformAdminOrgDetails; @@ -229,10 +222,10 @@ export class AgentServiceService { const socket: Socket = await this.initSocketConnection(`${process.env.SOCKET_HOST}`); this.emitAgentSpinupInitiatedEvent(agentSpinupDto, socket); - const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; - agentProcess = await this.createOrgAgent(agentSpinUpStatus, userId); + const agentSpinUpStatus = AgentSpinUpStatus.WALLET_CREATED; + agentProcess = await this.createOrgAgent(agentSpinUpStatus, userId, agentSpinupDto.orgId); - // AFJ agent spin-up + // Credo agent spin-up this._agentSpinup( walletProvisionPayload, agentSpinupDto, @@ -298,7 +291,7 @@ export class AgentServiceService { const storeAgentConfig = await this.agentServiceRepository.storeOrgAgentDetails({ did, isDidPublic: true, - agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, + agentSpinUpStatus: AgentSpinUpStatus.DID_CREATED, walletName, agentsTypeId, orgId, @@ -441,9 +434,9 @@ export class AgentServiceService { return socket; } - async createOrgAgent(agentSpinUpStatus: AgentSpinUpStatus, userId: string): Promise { + async createOrgAgent(agentSpinUpStatus: AgentSpinUpStatus, userId: string, orgId: string): Promise { try { - const agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, userId); + const agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, userId, orgId); this.logger.log(`Organization agent created with status: ${agentSpinUpStatus}`); return agentProcess; } catch (error) { @@ -488,7 +481,7 @@ export class AgentServiceService { ledgerId: string[], agentProcess: ICreateOrgAgent ): Promise { - let ledgerIdData = []; + let ledgerIdData; try { if (agentSpinupDto.method !== DidMethod.KEY && agentSpinupDto.method !== DidMethod.WEB) { @@ -626,7 +619,7 @@ export class AgentServiceService { did: '', verkey: '', isDidPublic: true, - agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, + agentSpinUpStatus: AgentSpinUpStatus.DID_CREATED, walletName: payload.walletName, agentsTypeId: payload.agentsTypeId, orgId: payload.orgId, @@ -742,32 +735,20 @@ export class AgentServiceService { * @param user * @returns Get agent status */ - async createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { + async createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { try { - const agentStatusResponse = { - agentSpinupStatus: AgentSpinUpStatus.PROCESSED - }; const getOrgAgent = await this.agentServiceRepository.getAgentDetails(payload.orgId); - if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { - this.logger.error(`Your wallet is already been created.`); + if (AgentSpinUpStatus.WALLET_CREATED === getOrgAgent?.agentSpinUpStatus) { + this.logger.error(`Your wallet is already created.`); throw new ConflictException(ResponseMessages.agent.error.walletAlreadyCreated, { cause: new Error(), description: ResponseMessages.errorMessages.conflict }); } - - if (AgentSpinUpStatus.PROCESSED === getOrgAgent?.agentSpinUpStatus) { - this.logger.error(`Your wallet is already processing.`); - throw new ConflictException(ResponseMessages.agent.error.walletAlreadyProcessing, { - cause: new Error(), - description: ResponseMessages.errorMessages.conflict - }); - } - // Create tenant - this._createTenant(payload, user); - return agentStatusResponse; + const createdTenant = await this._createTenant(payload, user); + return createdTenant; } catch (error) { this.logger.error(`error in create tenant : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); @@ -780,7 +761,7 @@ export class AgentServiceService { * @param user * @returns Get agent status */ - async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { + async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { let agentProcess; let ledgerIdData = []; try { @@ -802,18 +783,19 @@ export class AgentServiceService { } ledgerIdData = await this.agentServiceRepository.getLedgerDetails(ledger); - const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; + const agentSpinUpStatus = AgentSpinUpStatus.WALLET_CREATED; // Create and stored agent details - agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id); + agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id, payload.orgId); // Get platform admin details const platformAdminSpinnedUp = await this.getPlatformAdminAndNotify(payload.clientSocketId); payload.endpoint = platformAdminSpinnedUp.org_agents[0].agentEndPoint; - // Create tenant wallet and DID + // Create tenant wallet const tenantDetails = await this.createTenantAndNotify(payload, platformAdminSpinnedUp); - if (!tenantDetails?.walletResponseDetails?.id || !tenantDetails?.DIDCreationOption?.did) { + + if (!tenantDetails?.walletResponseDetails?.id) { this.logger.error(`Error in getting wallet id and wallet did`); throw new NotFoundException(ResponseMessages.agent.error.notAbleToSpinUpAgent, { cause: new Error(), @@ -821,7 +803,7 @@ export class AgentServiceService { }); } - if (AgentSpinUpStatus.COMPLETED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { + if (AgentSpinUpStatus.DID_CREATED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { this.logger.error(`Platform-admin agent is not spun-up`); throw new NotFoundException(ResponseMessages.agent.error.platformAdminNotAbleToSpinp, { cause: new Error(), @@ -833,20 +815,17 @@ export class AgentServiceService { // Get agent type details const agentTypeId = await this.agentServiceRepository.getAgentTypeId(AgentType.AFJ); const storeOrgAgentData: IStoreOrgAgentDetails = { - did: tenantDetails.DIDCreationOption.did, - isDidPublic: true, - didDoc: tenantDetails.DIDCreationOption.didDocument || tenantDetails.DIDCreationOption.didDoc, //changed the didDoc into didDocument - agentSpinUpStatus: AgentSpinUpStatus.COMPLETED, + agentSpinUpStatus: AgentSpinUpStatus.WALLET_CREATED, agentsTypeId: agentTypeId, orgId: payload.orgId, agentEndPoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, orgAgentTypeId, tenantId: tenantDetails.walletResponseDetails['id'], walletName: payload.label, - ledgerId: ledgerIdData.map((item) => item.id), id: agentProcess?.id, apiKey: await this.commonService.dataEncryption(tenantDetails.walletResponseDetails['token']) }; + // Get organization data const getOrganization = await this.agentServiceRepository.getOrgDetails(payload.orgId); @@ -854,29 +833,16 @@ export class AgentServiceService { const orgAgentDetails = await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); - const createdDidDetails = { - orgId: payload.orgId, - did: tenantDetails.DIDCreationOption.did, - didDocument: tenantDetails.DIDCreationOption.didDocument || tenantDetails.DIDCreationOption.didDoc, - isPrimaryDid: true, - orgAgentId: orgAgentDetails.id, - userId: user.id - }; - - await this.agentServiceRepository.storeDidDetails(createdDidDetails); - this.notifyClientSocket('invitation-url-creation-started', payload.clientSocketId); // Create the legacy connection invitation await this._createConnectionInvitation(payload.orgId, user, getOrganization.name); this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); + + return orgAgentDetails; } catch (error) { this.handleError(error, payload.clientSocketId); - - if (agentProcess && agentProcess?.id) { - this.agentServiceRepository.removeOrgAgent(agentProcess?.id); - } throw error; } } @@ -930,7 +896,6 @@ export class AgentServiceService { } } } - const getApiKey = await this.getOrgAgentApiKey(orgId); const url = this.constructUrl(agentDetails); @@ -961,6 +926,9 @@ export class AgentServiceService { if (isPrimaryDid) { await this.setPrimaryDidAndLedger(orgId, storeDidDetails, createDidPayload.network, createDidPayload.method); } + if (agentDetails.agentSpinUpStatus === AgentSpinUpStatus.WALLET_CREATED) { + await this.agentServiceRepository.updateAgentSpinupStatus(orgId); + } return storeDidDetails; } catch (error) { @@ -1119,20 +1087,12 @@ export class AgentServiceService { platformAdminSpinnedUp.org_agents[0].agentEndPoint, getDcryptedToken ); - if (!walletResponseDetails && !walletResponseDetails.id && !walletResponseDetails.token) { + if (!walletResponseDetails || !walletResponseDetails.id || !walletResponseDetails.token) { throw new InternalServerErrorException('Error while creating the wallet'); } - const didCreateOption = { - didPayload: WalletSetupPayload, - agentEndpoint: platformAdminSpinnedUp.org_agents[0].agentEndPoint, - apiKey: walletResponseDetails.token + return { + walletResponseDetails }; - const DIDCreationOption = await this._createDID(didCreateOption); - if (!DIDCreationOption) { - throw new InternalServerErrorException('Error while creating the wallet'); - } - - return { walletResponseDetails, DIDCreationOption }; } // diff --git a/apps/agent-service/src/repositories/agent-service.repository.ts b/apps/agent-service/src/repositories/agent-service.repository.ts index f561694e5..bdf32a8f2 100644 --- a/apps/agent-service/src/repositories/agent-service.repository.ts +++ b/apps/agent-service/src/repositories/agent-service.repository.ts @@ -1,5 +1,16 @@ -import { PrismaService } from '@credebl/prisma-service'; +import { AgentSpinUpStatus, AgentType, PrismaTables } from '@credebl/enum/enum'; import { ConflictException, Injectable, Logger } from '@nestjs/common'; +import { + ICreateOrgAgent, + ILedgers, + IOrgAgent, + IOrgAgentsResponse, + IOrgLedgers, + IStoreDidDetails, + IStoreOrgAgentDetails, + LedgerNameSpace, + OrgDid +} from '../interface/agent-service.interface'; /* eslint-disable camelcase */ import { Prisma, @@ -12,19 +23,8 @@ import { platform_config, user } from '@prisma/client'; -import { - ICreateOrgAgent, - ILedgers, - IOrgAgent, - IOrgAgentsResponse, - IOrgLedgers, - IStoreAgent, - IStoreDidDetails, - IStoreOrgAgentDetails, - LedgerNameSpace, - OrgDid -} from '../interface/agent-service.interface'; -import { AgentType, PrismaTables } from '@credebl/enum/enum'; + +import { PrismaService } from '@credebl/prisma-service'; @Injectable() export class AgentServiceRepository { @@ -100,13 +100,14 @@ export class AgentServiceRepository { } // eslint-disable-next-line camelcase - async createOrgAgent(agentSpinUpStatus: number, userId: string): Promise { + async createOrgAgent(agentSpinUpStatus: number, userId: string, orgId: string): Promise { try { return this.prisma.org_agents.create({ data: { agentSpinUpStatus, createdBy: userId, - lastChangedBy: userId + lastChangedBy: userId, + orgId }, select: { id: true @@ -155,12 +156,20 @@ export class AgentServiceRepository { * @returns */ // eslint-disable-next-line camelcase - async storeOrgAgentDetails(storeOrgAgentDetails: IStoreOrgAgentDetails): Promise { + async storeOrgAgentDetails(storeOrgAgentDetails: IStoreOrgAgentDetails): Promise<{ + orgId: string; + agentSpinUpStatus: number; + agentEndPoint: string; + tenantId: string; + walletName: string; + id: string; + }> { try { - const { id, userId, ledgerId, did, didDoc, ...commonFields } = storeOrgAgentDetails; + const { id, orgId, did, userId, ledgerId, didDoc, ...commonFields } = storeOrgAgentDetails; const firstLedgerId = Array.isArray(ledgerId) ? ledgerId[0] : null; const data = { ...commonFields, + orgId, ledgerId: firstLedgerId, createdBy: userId, lastChangedBy: userId, @@ -169,14 +178,32 @@ export class AgentServiceRepository { }; // eslint-disable-next-line camelcase - const query: Promise = id + const query = id ? this.prisma.org_agents.update({ where: { id }, - data + data, + select: { + id: true, + orgId: true, + agentSpinUpStatus: true, + agentEndPoint: true, + tenantId: true, + walletName: true + } }) - : this.prisma.org_agents.create({ data }); - - return { id: (await query).id }; + : this.prisma.org_agents.create({ + data, + select: { + id: true, + orgId: true, + agentSpinUpStatus: true, + agentEndPoint: true, + tenantId: true, + walletName: true + } + }); + + return query; } catch (error) { this.logger.error(`[storeAgentDetails] - store agent details: ${JSON.stringify(error)}`); throw error; @@ -233,7 +260,30 @@ export class AgentServiceRepository { } } + /** + * Update agent spinup status + * @param orgId + */ + // eslint-disable-next-line camelcase + // TODO: Combine these org_agents update functions into one common update method. + async updateAgentSpinupStatus(orgId: string): Promise { + try { + return await this.prisma.org_agents.update({ + where: { + orgId + }, + data: { + agentSpinUpStatus: AgentSpinUpStatus.DID_CREATED + } + }); + } catch (error) { + this.logger.error(`[updateAgentSpinupStatus] - Update agent spinup status: ${JSON.stringify(error)}`); + throw error; + } + } + // eslint-disable-next-line camelcase + // TODO: Combine these org_agents update functions into one common update method. async updateLedgerId(orgId: string, ledgerId: string): Promise { try { return await this.prisma.org_agents.update({ diff --git a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts index e26131431..6ca54a5e5 100644 --- a/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-tenant.dto.ts @@ -1,28 +1,28 @@ -import { trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; + import { Transform } from 'class-transformer'; -import { MaxLength, IsString, MinLength, IsOptional } from 'class-validator'; -import { CreateDidDto } from './create-did.dto'; -export class CreateTenantDto extends CreateDidDto { - @ApiProperty() - @MaxLength(25, { message: 'Maximum length for label must be 25 characters.' }) - @IsString({ message: 'label must be in string format.' }) - @Transform(({ value }) => trim(value)) - @MinLength(2, { message: 'Minimum length for label must be 2 characters.' }) - label: string; +import { trim } from '@credebl/common/cast.helper'; + +export class CreateTenantDto { + @ApiProperty() + @MaxLength(25, { message: 'Maximum length for label must be 25 characters.' }) + @IsString({ message: 'label must be in string format.' }) + @Transform(({ value }) => trim(value)) + @MinLength(2, { message: 'Minimum length for label must be 2 characters.' }) + label: string; - @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) - @IsOptional() - @ApiPropertyOptional() - @IsString({ message: 'did must be in string format.' }) - clientSocketId?: string; + @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'clientSocketId must be in string format.' }) + clientSocketId?: string; - @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) - @IsOptional() - @ApiPropertyOptional() - @IsString({ message: 'did must be in string format.' }) - did?: string; + @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) + @IsOptional() + @ApiPropertyOptional() + @IsString({ message: 'did must be in string format.' }) + did?: string; - orgId: string; - -} \ No newline at end of file + orgId: string; +} diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index bd8f5f6fd..82da49781 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -223,7 +223,7 @@ export const ResponseMessages = { }, agent: { success: { - create: 'Agent process initiated successfully. Please wait', + create: 'Organization wallet created successfully', createWallet: 'Wallet created successfully', createDid: 'Did created successfully', health: 'Agent health details retrieved successfully.', diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 9dc8b31ca..5a4949e7e 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -87,8 +87,8 @@ export enum OrgAgentType { export enum AgentSpinUpStatus { PENDING = 0, - PROCESSED = 1, - COMPLETED = 2 + WALLET_CREATED = 1, + DID_CREATED = 2 } export enum UserCertificateId {