diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 81e08f488..d7d9552e7 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -1,7 +1,7 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { AgentServiceService } from './agent-service.service'; -import { AgentStatus, GetCredDefAgentRedirection, GetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, OutOfBandCredentialOffer } from './interface/agent-service.interface'; +import { IAgentStatus, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer } from './interface/agent-service.interface'; import { IConnectionDetails, IUserRequestInterface } from './interface/agent-service.interface'; import { ISendProofRequestPayload } from './interface/agent-service.interface'; import { user } from '@prisma/client'; @@ -16,9 +16,7 @@ export class AgentServiceController { } @MessagePattern({ cmd: 'create-tenant' }) - async createTenant(payload: { createTenantDto: ITenantDto, user: IUserRequestInterface }): Promise<{ - agentSpinupStatus: number; - }> { + async createTenant(payload: { createTenantDto: ITenantDto, user: IUserRequestInterface }): Promise { return this.agentServiceService.createTenant(payload.createTenantDto, payload.user); } @@ -28,7 +26,7 @@ export class AgentServiceController { } @MessagePattern({ cmd: 'agent-get-schema' }) - async getSchemaById(payload: GetSchemaAgentRedirection): Promise { + async getSchemaById(payload: IGetSchemaAgentRedirection): Promise { return this.agentServiceService.getSchemaById(payload); } @@ -39,7 +37,7 @@ export class AgentServiceController { } @MessagePattern({ cmd: 'agent-get-credential-definition' }) - async getCredentialDefinitionById(payload: GetCredDefAgentRedirection): Promise { + async getCredentialDefinitionById(payload: IGetCredDefAgentRedirection): Promise { return this.agentServiceService.getCredentialDefinitionById(payload); } @@ -100,7 +98,7 @@ export class AgentServiceController { * @returns Get agent health */ @MessagePattern({ cmd: 'agent-health' }) - async getAgentHealth(payload: { user: user, orgId: string }): Promise { + async getAgentHealth(payload: { user: user, orgId: string }): Promise { return this.agentServiceService.getAgentHealthDetails(payload.orgId); } @@ -134,7 +132,7 @@ export class AgentServiceController { } @MessagePattern({ cmd: 'agent-out-of-band-credential-offer' }) - async outOfBandCredentialOffer(payload: { outOfBandIssuancePayload: OutOfBandCredentialOffer, url: string, apiKey: string }): Promise { + async outOfBandCredentialOffer(payload: { outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, apiKey: string }): Promise { return this.agentServiceService.outOfBandCredentialOffer(payload.outOfBandIssuancePayload, payload.url, payload.apiKey); } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index a1a779238..44564eabd 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -6,6 +6,7 @@ /* eslint-disable camelcase */ import { BadRequestException, + ConflictException, HttpException, Inject, Injectable, @@ -18,7 +19,7 @@ import * as dotenv from 'dotenv'; import * as fs from 'fs'; import { catchError, map } from 'rxjs/operators'; dotenv.config(); -import { GetCredDefAgentRedirection, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, OutOfBandCredentialOffer, AgentStatus } from './interface/agent-service.interface'; +import { IGetCredDefAgentRedirection, IAgentSpinupDto, IStoreOrgAgentDetails, ITenantCredDef, ITenantDto, ITenantSchema, IWalletProvision, ISendProofRequestPayload, IIssuanceCreateOffer, IOutOfBandCredentialOffer, IAgentSpinUpSatus, ICreateTenant, IAgentStatus, ICreateOrgAgent, IOrgAgentsResponse } from './interface/agent-service.interface'; import { AgentSpinUpStatus, AgentType, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { IConnectionDetails, IUserRequestInterface } from './interface/agent-service.interface'; import { AgentServiceRepository } from './repositories/agent-service.repository'; @@ -146,16 +147,16 @@ export class AgentServiceService { private async processWalletProvision(agentSpinupDto: IAgentSpinupDto, user: IUserRequestInterface): Promise { let platformAdminUser; let userId: string; - let agentProcess: org_agents; + let agentProcess: ICreateOrgAgent; + let getOrgAgent; try { - const [getOrgAgent, platformConfig, getAgentType, ledgerIdData, orgData] = await Promise.all([ - this.agentServiceRepository.getAgentDetails(agentSpinupDto.orgId), + const [platformConfig, getAgentType, ledgerIdData] = await Promise.all([ this.agentServiceRepository.getPlatformConfigDetails(), this.agentServiceRepository.getAgentTypeDetails(), - this.agentServiceRepository.getLedgerDetails(agentSpinupDto.ledgerName ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet]), - this.agentServiceRepository.getOrgDetails(agentSpinupDto.orgId) + this.agentServiceRepository.getLedgerDetails(agentSpinupDto.ledgerName ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet]) ]); + let orgData; if (!user?.userId && agentSpinupDto?.platformAdminEmail) { platformAdminUser = await this.agentServiceRepository.getPlatfomAdminUser(agentSpinupDto?.platformAdminEmail); @@ -165,7 +166,16 @@ export class AgentServiceService { userId = user?.userId; } - agentSpinupDto.ledgerId = agentSpinupDto.ledgerId?.length ? agentSpinupDto.ledgerId : ledgerIdData.map(ledger => ledger.id); + const platformAdminOrgDetails = await this.agentServiceRepository.getPlatfomOrg(agentSpinupDto?.orgName); + if (agentSpinupDto.orgId) { + getOrgAgent = await this.agentServiceRepository.getAgentDetails(agentSpinupDto.orgId); + orgData = await this.agentServiceRepository.getOrgDetails(agentSpinupDto.orgId); + } else { + getOrgAgent = await this.agentServiceRepository.getAgentDetails(platformAdminOrgDetails); + orgData = await this.agentServiceRepository.getOrgDetails(platformAdminOrgDetails); + } + + agentSpinupDto.ledgerId = agentSpinupDto.ledgerId?.length ? agentSpinupDto.ledgerId : ledgerIdData.map(ledger => ledger?.id); const ledgerDetails = await this.agentServiceRepository.getGenesisUrl(agentSpinupDto.ledgerId); if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { @@ -176,8 +186,8 @@ export class AgentServiceService { throw new BadRequestException('Your wallet is already processing.'); } + if (!agentSpinupDto.orgId) { - const platformAdminOrgDetails = await this.agentServiceRepository.getPlatfomOrg(agentSpinupDto?.orgName); if (platformAdminOrgDetails) { agentSpinupDto.orgId = platformAdminOrgDetails; @@ -229,7 +239,7 @@ export class AgentServiceService { } } - validateAgentProcess(agentProcess: org_agents): void { + validateAgentProcess(agentProcess: ICreateOrgAgent): void { try { if (!agentProcess) { throw new BadRequestException('Agent process is invalid or not in a completed state.'); @@ -272,11 +282,11 @@ export class AgentServiceService { const escapedJsonString = JSON.stringify(ledgerArray).replace(/"/g, '\\"'); const walletProvisionPayload: IWalletProvision = { - orgId: orgData.id, + orgId: orgData?.id, externalIp, - walletName: agentSpinupDto.walletName, - walletPassword: agentSpinupDto.walletPassword, - seed: agentSpinupDto.seed, + walletName: agentSpinupDto?.walletName, + walletPassword: agentSpinupDto?.walletPassword, + seed: agentSpinupDto?.seed, webhookEndpoint: apiEndpoint, walletStorageHost: process.env.WALLET_STORAGE_HOST || '', walletStoragePort: process.env.WALLET_STORAGE_PORT || '', @@ -285,7 +295,7 @@ export class AgentServiceService { internalIp: await this._validateInternalIp(platformConfig, controllerIp), containerName: orgData.name.split(' ').join('_'), agentType: AgentType.AFJ, - orgName: orgData.name, + orgName: orgData?.name, indyLedger: escapedJsonString, afjVersion: process.env.AFJ_VERSION || '', protocol: process.env.AGENT_PROTOCOL || '', @@ -307,7 +317,7 @@ export class AgentServiceService { return socket; } - async createOrgAgent(agentSpinUpStatus: AgentSpinUpStatus, userId: string): Promise { + async createOrgAgent(agentSpinUpStatus: AgentSpinUpStatus, userId: string): Promise { try { const agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, userId); this.logger.log(`Organization agent created with status: ${agentSpinUpStatus}`); @@ -319,7 +329,7 @@ export class AgentServiceService { } } - private async handleErrorOnWalletProvision(agentSpinupDto: IAgentSpinupDto, error: Error, agentProcess: org_agents): Promise { + private async handleErrorOnWalletProvision(agentSpinupDto: IAgentSpinupDto, error: Error, agentProcess: ICreateOrgAgent): Promise { if (agentProcess) { const socket = await this.initSocketConnection(`${process.env.SOCKET_HOST}`); @@ -342,7 +352,7 @@ export class AgentServiceService { } } - async _agentSpinup(walletProvisionPayload: IWalletProvision, agentSpinupDto: IAgentSpinupDto, orgApiKey: string, orgData: organisation, user: IUserRequestInterface, socket: Socket, ledgerId: string[], agentProcess: org_agents): Promise { + async _agentSpinup(walletProvisionPayload: IWalletProvision, agentSpinupDto: IAgentSpinupDto, orgApiKey: string, orgData: organisation, user: IUserRequestInterface, socket: Socket, ledgerId: string[], agentProcess: ICreateOrgAgent): Promise { try { const walletProvision = await this._walletProvision(walletProvisionPayload); @@ -372,7 +382,7 @@ export class AgentServiceService { clientSocketId: agentSpinupDto.clientSocketId, ledgerId, did: agentSpinupDto.did, - id: agentProcess.id + id: agentProcess?.id }; const storeAgentDetails = await this._storeOrgAgentDetails(agentPayload); @@ -382,9 +392,9 @@ export class AgentServiceService { socket.emit('did-publish-process-completed', { clientId: agentSpinupDto.clientSocketId }); } - const getOrganization = await this.agentServiceRepository.getOrgDetails(orgData.id); + const getOrganization = await this.agentServiceRepository.getOrgDetails(orgData?.id); - await this._createLegacyConnectionInvitation(orgData.id, user, getOrganization.name); + await this._createLegacyConnectionInvitation(orgData?.id, user, getOrganization.name); if (agentSpinupDto.clientSocketId) { socket.emit('invitation-url-creation-success', { clientId: agentSpinupDto.clientSocketId }); @@ -449,7 +459,7 @@ export class AgentServiceService { agentId: payload.agentId, orgAgentTypeId, ledgerId: payload.ledgerId, - id: payload.id + id: payload?.id }; } @@ -550,53 +560,109 @@ export class AgentServiceService { } } - async createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise<{ - agentSpinupStatus: number; - }> { + /** + * Create tenant (Shared agent) + * @param payload + * @param user + * @returns Get agent status + */ + async createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { + try { - const agentStatusResponse = { - agentSpinupStatus: AgentSpinUpStatus.PROCESSED - }; + 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.`); + 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 } + ); + } - this._createTenant(payload, user); - return agentStatusResponse; + // Create tenant + this._createTenant(payload, user); + return agentStatusResponse; + } catch (error) { + this.logger.error(`error in create tenant : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } } - async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { + /** + * Create tenant (Shared agent) + * @param payload + * @param user + * @returns Get agent status + */ + async _createTenant(payload: ITenantDto, user: IUserRequestInterface): Promise { let agentProcess; try { + + // Get orgaization agent details by orgId const getOrgAgent = await this.agentServiceRepository.getAgentDetails(payload.orgId); if (AgentSpinUpStatus.COMPLETED === getOrgAgent?.agentSpinUpStatus) { this.logger.error(`Your wallet has already been created.`); - throw new BadRequestException('Your wallet has already been created.'); + throw new BadRequestException( + ResponseMessages.agent.error.walletAlreadyCreated, + { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } + ); } if (AgentSpinUpStatus.PROCESSED === getOrgAgent?.agentSpinUpStatus) { this.logger.error(`Your wallet has already processing.`); - throw new BadRequestException('Your wallet has already processing.'); + throw new BadRequestException( + ResponseMessages.agent.error.walletAlreadyProcessing, + { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } + ); } + // Get ledgers details const ledgerIdData = await this.agentServiceRepository.getLedgerDetails(Ledgers.Indicio_Demonet); - const ledgerIds = ledgerIdData.map(ledger => ledger.id); + const ledgerIds = ledgerIdData.map(ledger => ledger?.id); payload.ledgerId = !payload.ledgerId || 0 === payload.ledgerId?.length ? ledgerIds : payload.ledgerId; const agentSpinUpStatus = AgentSpinUpStatus.PROCESSED; - agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user.id); + // Create and stored agent details + agentProcess = await this.agentServiceRepository.createOrgAgent(agentSpinUpStatus, user?.id); + // Get platform admin details const platformAdminSpinnedUp = await this.getPlatformAdminAndNotify(payload.clientSocketId); + + // Get genesis URLs by ledger Id const ledgerDetails: ledgers[] = await this.agentServiceRepository.getGenesisUrl(payload.ledgerId); for (const iterator of ledgerDetails) { + + // Create tenant in agent controller const tenantDetails = await this.createTenantAndNotify(payload, iterator, platformAdminSpinnedUp); if (AgentSpinUpStatus.COMPLETED !== platformAdminSpinnedUp.org_agents[0].agentSpinUpStatus) { - throw new NotFoundException('Platform-admin agent is not spun-up'); + this.logger.error(`Platform-admin agent is not spun-up`); + throw new NotFoundException( + ResponseMessages.agent.error.platformAdminNotAbleToSpinp, + { cause: new Error(), description: ResponseMessages.errorMessages.notFound } + ); } + // Get org agent type details by shared agent const orgAgentTypeId = await this.agentServiceRepository.getOrgAgentTypeDetails(OrgAgentType.SHARED); + + // Get agent type details by AFJ agent const agentTypeId = await this.agentServiceRepository.getAgentTypeId(AgentType.AFJ); const storeOrgAgentData: IStoreOrgAgentDetails = { @@ -611,20 +677,21 @@ export class AgentServiceService { tenantId: tenantDetails['tenantRecord']['id'], walletName: payload.label, ledgerId: payload.ledgerId, - id: agentProcess.id + id: agentProcess?.id }; + // Get organization data const getOrganization = await this.agentServiceRepository.getOrgDetails(payload.orgId); this.notifyClientSocket('agent-spinup-process-completed', payload.clientSocketId); - const saveTenant = await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); + await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); this.notifyClientSocket('invitation-url-creation-started', payload.clientSocketId); + + // Create the legacy connection invitation this._createLegacyConnectionInvitation(payload.orgId, user, getOrganization.name); this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); - - return saveTenant; } } catch (error) { this.handleError(error, payload.clientSocketId); @@ -632,12 +699,11 @@ export class AgentServiceService { if (agentProcess && agentProcess?.id) { this.agentServiceRepository.removeOrgAgent(agentProcess?.id); } - - throw new RpcException(error.response ? error.response : error); + throw error; } } - private async getPlatformAdminAndNotify(clientSocketId: string | undefined): Promise { + private async getPlatformAdminAndNotify(clientSocketId: string | undefined): Promise { const socket = await this.createSocketInstance(); if (clientSocketId) { socket.emit('agent-spinup-process-initiated', { clientId: clientSocketId }); @@ -646,13 +712,24 @@ export class AgentServiceService { const platformAdminSpinnedUp = await this.agentServiceRepository.platformAdminAgent(CommonConstants.PLATFORM_ADMIN_ORG); if (!platformAdminSpinnedUp) { - throw new InternalServerErrorException('Agent not able to spin-up'); + this.logger.error(`Agent not able to spin-up`); + throw new BadRequestException( + ResponseMessages.agent.error.notAbleToSpinp, + { cause: new Error(), description: ResponseMessages.errorMessages.serverError } + ); } return platformAdminSpinnedUp; } - private async createTenantAndNotify(payload: ITenantDto, ledgerIds: ledgers, platformAdminSpinnedUp: organisation & { org_agents: org_agents[] }): Promise { + /** + * Create tenant on the agent + * @param payload + * @param ledgerIds + * @param platformAdminSpinnedUp + * @returns Get tanant status + */ + private async createTenantAndNotify(payload: ITenantDto, ledgerIds: ledgers, platformAdminSpinnedUp: IOrgAgentsResponse): Promise { const socket = await this.createSocketInstance(); if (payload.clientSocketId) { socket.emit('agent-spinup-process-initiated', { clientId: payload.clientSocketId }); @@ -667,6 +744,8 @@ export class AgentServiceService { }; const apiKey = ''; + + // Invoke an API request from the agent to create multi-tenant agent const tenantDetails = await this.commonService.httpPost( `${platformAdminSpinnedUp.org_agents[0].agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_TENANT}`, createTenantOptions, @@ -824,7 +903,7 @@ export class AgentServiceService { } } - async getCredentialDefinitionById(payload: GetCredDefAgentRedirection): Promise { + async getCredentialDefinitionById(payload: IGetCredDefAgentRedirection): Promise { try { let credDefResponse; @@ -978,7 +1057,7 @@ export class AgentServiceService { * @param orgId * @returns agent status */ - async getAgentHealthDetails(orgId: string): Promise { + async getAgentHealthDetails(orgId: string): Promise { try { // Get organization agent details @@ -1086,7 +1165,7 @@ export class AgentServiceService { } } - async outOfBandCredentialOffer(outOfBandIssuancePayload: OutOfBandCredentialOffer, url: string, apiKey: string): Promise { + async outOfBandCredentialOffer(outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, apiKey: string): Promise { try { const sendOutOfbandCredentialOffer = await this.commonService .httpPost(url, outOfBandIssuancePayload, { headers: { 'x-api-key': apiKey } }) diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 04a8a885a..358d3083c 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -17,9 +17,9 @@ export interface IAgentSpinupDto { platformAdminEmail?: string; } -export interface OutOfBandCredentialOffer { +export interface IOutOfBandCredentialOffer { emailId: string; - attributes: Attributes[]; + attributes: IAttributes[]; credentialDefinitionId: string; comment: string; protocolVersion?: string; @@ -57,17 +57,17 @@ export interface ITenantSchemaDto { issuerId: string; } -export interface GetSchemaAgentRedirection { +export interface IGetSchemaAgentRedirection { schemaId?: string; tenantId?: string; - payload?: GetSchemaFromTenantPayload; + payload?: IGetSchemaFromTenantPayload; apiKey?: string; agentEndPoint?: string; agentType?: string; method?: string; } -export interface GetSchemaFromTenantPayload { +export interface IGetSchemaFromTenantPayload { schemaId: string; } @@ -89,17 +89,17 @@ export interface ITenantCredDefDto { issuerId: string; } -export interface GetCredDefAgentRedirection { +export interface IGetCredDefAgentRedirection { credentialDefinitionId?: string; tenantId?: string; - payload?: GetCredDefFromTenantPayload; + payload?: IGetCredDefFromTenantPayload; apiKey?: string; agentEndPoint?: string; agentType?: string; method?: string; } -export interface GetCredDefFromTenantPayload { +export interface IGetCredDefFromTenantPayload { credentialDefinitionId: string; } @@ -220,17 +220,17 @@ export interface ITenantCredDefDto { issuerId: string; } -export interface GetCredDefAgentRedirection { +export interface IGetCredDefAgentRedirection { credentialDefinitionId?: string; tenantId?: string; - payload?: GetCredDefFromTenantPayload; + payload?: IGetCredDefFromTenantPayload; apiKey?: string; agentEndPoint?: string; agentType?: string; method?: string; } -export interface GetCredDefFromTenantPayload { +export interface IGetCredDefFromTenantPayload { credentialDefinitionId: string; } @@ -247,10 +247,10 @@ export interface ICredentialFormats { } export interface IIndy { - attributes: Attributes[]; + attributes: IAttributes[]; } -export interface Attributes { +export interface IAttributes { name: string; value: string; } @@ -261,7 +261,7 @@ export interface ISendProofRequestPayload { autoAcceptProof: string; } -export interface AgentStatus { +export interface IAgentStatus { label: string; endpoints: string[]; isInitialized: boolean; @@ -300,3 +300,58 @@ interface IRequestedRestriction { cred_def_id: string; } +export interface IAgentSpinUpSatus { + agentSpinupStatus: number; +} + +interface IWalletConfig { + id: string; + key: string; + keyDerivationMethod: string; +} + +interface IConfig { + label: string; + walletConfig: IWalletConfig; +} + +interface ITenantRecord { + _tags: string; + metadata: string; + id: string; + createdAt: string; + config: IConfig; + updatedAt: string; +} + +export interface ICreateTenant { + tenantRecord: ITenantRecord; + did: string; + verkey: string; +} + +export interface IOrgAgent { + agentSpinUpStatus: number; +} + +export interface IOrgLedgers { + id: string; +} + +export interface ICreateOrgAgent { + id: string; +} + +interface IOrgAgentEndPoint { + agentSpinUpStatus: number; + agentEndPoint: string +} + +export interface IOrgAgentsResponse { + org_agents: IOrgAgentEndPoint[]; +} + + +export interface IStoreAgent { + id: string; +} \ No newline at end of file diff --git a/apps/agent-service/src/repositories/agent-service.repository.ts b/apps/agent-service/src/repositories/agent-service.repository.ts index e2ac3cb8b..11b124c96 100644 --- a/apps/agent-service/src/repositories/agent-service.repository.ts +++ b/apps/agent-service/src/repositories/agent-service.repository.ts @@ -1,317 +1,332 @@ import { PrismaService } from '@credebl/prisma-service'; import { Injectable, Logger } from '@nestjs/common'; // eslint-disable-next-line camelcase -import { Prisma, ledgers, org_agents, organisation, platform_config, user } from '@prisma/client'; -import { IStoreOrgAgentDetails } from '../interface/agent-service.interface'; +import { ledgers, org_agents, organisation, platform_config, user } from '@prisma/client'; +import { ICreateOrgAgent, IStoreOrgAgentDetails, IOrgAgent, IOrgAgentsResponse, IOrgLedgers, IStoreAgent } from '../interface/agent-service.interface'; import { AgentType } from '@credebl/enum/enum'; @Injectable() export class AgentServiceRepository { - constructor(private readonly prisma: PrismaService, private readonly logger: Logger) { } + constructor(private readonly prisma: PrismaService, private readonly logger: Logger) { } - /** - * Get platform config details - * @returns - */ - // eslint-disable-next-line camelcase - async getPlatformConfigDetails(): Promise { - try { + /** + * Get platform config details + * @returns + */ + // eslint-disable-next-line camelcase + async getPlatformConfigDetails(): Promise { + try { - return this.prisma.platform_config.findFirst(); + return this.prisma.platform_config.findFirst(); - } catch (error) { - this.logger.error(`[getPlatformConfigDetails] - error: ${JSON.stringify(error)}`); - throw error; + } catch (error) { + this.logger.error(`[getPlatformConfigDetails] - error: ${JSON.stringify(error)}`); + throw error; + } } - } - /** - * Get genesis url - * @param id - * @returns - */ - async getGenesisUrl(ledgerId: string[]): Promise { - try { - - const genesisData = await this.prisma.ledgers.findMany({ - where: { - id: { - in: ledgerId - } + /** + * Get genesis url + * @param id + * @returns + */ + async getGenesisUrl(ledgerId: string[]): Promise { + try { + + const genesisData = await this.prisma.ledgers.findMany({ + where: { + id: { + in: ledgerId + } + } + }); + + return genesisData; + } catch (error) { + this.logger.error(`[getGenesisUrl] - get genesis URL: ${JSON.stringify(error)}`); + throw error; } - }); - - return genesisData; - } catch (error) { - this.logger.error(`[getGenesisUrl] - get genesis URL: ${JSON.stringify(error)}`); - throw error; } - } - - /** - * Get organization details - * @param id - * @returns - */ - async getOrgDetails(id: string): Promise { - try { - const oranizationDetails = await this.prisma.organisation.findFirst({ - where: { - id + /** + * Get organization details + * @param id + * @returns + */ + async getOrgDetails(id: string): Promise { + try { + + if (id) { + const oranizationDetails = await this.prisma.organisation.findUnique({ + where: { + id + } + }); + return oranizationDetails; + } + } catch (error) { + this.logger.error(`[getOrgDetails] - get organization details: ${JSON.stringify(error)}`); + throw error; } - }); - return oranizationDetails; - } catch (error) { - this.logger.error(`[getOrgDetails] - get organization details: ${JSON.stringify(error)}`); - throw error; } - } - // eslint-disable-next-line camelcase - async createOrgAgent(agentSpinUpStatus: number, userId: string): Promise { - try { - - return this.prisma.org_agents.create({ - data: { - agentSpinUpStatus, - createdBy: userId, - lastChangedBy: userId + // eslint-disable-next-line camelcase + async createOrgAgent(agentSpinUpStatus: number, userId: string): Promise { + try { + + return this.prisma.org_agents.create({ + data: { + agentSpinUpStatus, + createdBy: userId, + lastChangedBy: userId + }, + select: { + id: true + } + }); + } catch (error) { + this.logger.error(`[createOrgAgent] - create agent details: ${JSON.stringify(error)}`); + throw error; } - }); - } catch (error) { - this.logger.error(`[createOrgAgent] - create agent details: ${JSON.stringify(error)}`); - throw error; } - } - - // eslint-disable-next-line camelcase - async removeOrgAgent(id: string): Promise { - try { - return this.prisma.org_agents.delete({ - where: { - id + // eslint-disable-next-line camelcase + async removeOrgAgent(id: string): Promise { + try { + if (id) { + + await this.prisma.org_agents.delete({ + where: { + id + } + }); + } + } catch (error) { + this.logger.error(`[removeOrgAgent] - remove org agent details: ${JSON.stringify(error)}`); + throw error; } - }); - } catch (error) { - this.logger.error(`[removeOrgAgent] - remove org agent details: ${JSON.stringify(error)}`); - throw error; } - } - /** - * Store agent details - * @param storeAgentDetails - * @returns - */ - // eslint-disable-next-line camelcase - async storeOrgAgentDetails(storeOrgAgentDetails: IStoreOrgAgentDetails): Promise { - try { - - return this.prisma.org_agents.update({ - where: { - id: storeOrgAgentDetails.id - }, - data: { - orgDid: storeOrgAgentDetails.did, - verkey: storeOrgAgentDetails.verkey, - isDidPublic: storeOrgAgentDetails.isDidPublic, - agentSpinUpStatus: storeOrgAgentDetails.agentSpinUpStatus, - walletName: storeOrgAgentDetails.walletName, - agentsTypeId: storeOrgAgentDetails.agentsTypeId, - orgId: storeOrgAgentDetails.orgId, - agentEndPoint: storeOrgAgentDetails.agentEndPoint, - agentId: storeOrgAgentDetails.agentId ? storeOrgAgentDetails.agentId : null, - orgAgentTypeId: storeOrgAgentDetails.orgAgentTypeId ? storeOrgAgentDetails.orgAgentTypeId : null, - tenantId: storeOrgAgentDetails.tenantId ? storeOrgAgentDetails.tenantId : null, - ledgerId: storeOrgAgentDetails.ledgerId[0] + /** + * Store agent details + * @param storeAgentDetails + * @returns + */ + // eslint-disable-next-line camelcase + async storeOrgAgentDetails(storeOrgAgentDetails: IStoreOrgAgentDetails): Promise { + try { + + return this.prisma.org_agents.update({ + where: { + id: storeOrgAgentDetails.id + }, + data: { + orgDid: storeOrgAgentDetails.did, + verkey: storeOrgAgentDetails.verkey, + isDidPublic: storeOrgAgentDetails.isDidPublic, + agentSpinUpStatus: storeOrgAgentDetails.agentSpinUpStatus, + walletName: storeOrgAgentDetails.walletName, + agentsTypeId: storeOrgAgentDetails.agentsTypeId, + orgId: storeOrgAgentDetails.orgId, + agentEndPoint: storeOrgAgentDetails.agentEndPoint, + agentId: storeOrgAgentDetails.agentId ? storeOrgAgentDetails.agentId : null, + orgAgentTypeId: storeOrgAgentDetails.orgAgentTypeId ? storeOrgAgentDetails.orgAgentTypeId : null, + tenantId: storeOrgAgentDetails.tenantId ? storeOrgAgentDetails.tenantId : null, + ledgerId: storeOrgAgentDetails.ledgerId[0] + }, + select: { + id: true + } + }); + } catch (error) { + this.logger.error(`[storeAgentDetails] - store agent details: ${JSON.stringify(error)}`); + throw error; } - }); - } catch (error) { - this.logger.error(`[storeAgentDetails] - store agent details: ${JSON.stringify(error)}`); - throw error; } - } - /** - * Get agent details - * @param orgId - * @returns - */ - // eslint-disable-next-line camelcase - async getAgentDetails(orgId: string): Promise { - try { - - const x = await this.prisma.org_agents.findFirst({ - where: { - orgId + /** + * Get agent details + * @param orgId + * @returns + */ + // eslint-disable-next-line camelcase + async getAgentDetails(orgId: string): Promise { + try { + + if (orgId) { + + return this.prisma.org_agents.findUnique({ + where: { + orgId + }, + select: { + agentSpinUpStatus: true + } + }); + } + + } catch (error) { + + this.logger.error(`[getAgentDetails] - get agent details: ${JSON.stringify(error)}`); + throw error; } - }); + } - return x; + // eslint-disable-next-line camelcase + async platformAdminAgent(platformOrg: string): Promise { + return this.prisma.organisation.findFirstOrThrow({ + where: { + name: platformOrg + }, + select: { + // eslint-disable-next-line camelcase + org_agents: { + select: { + agentSpinUpStatus: true, + agentEndPoint: true + } + } + } + }); + } - } catch (error) { - this.logger.error(`[getAgentDetails] - get agent details: ${JSON.stringify(error)}`); - throw error; - } - } - - // eslint-disable-next-line camelcase - async platformAdminAgent(platformOrg: string): Promise { - const platformAdminSpinnedUp = await this.prisma.organisation.findFirst({ - where: { - name: platformOrg - }, - include: { - // eslint-disable-next-line camelcase - org_agents: true - } - }); - return platformAdminSpinnedUp; - } - - /** - * Get agent details - * @param orgId - * @returns Agent health details - */ - // eslint-disable-next-line camelcase - async getOrgAgentDetails(orgId: string): Promise { - try { - - const oranizationAgentDetails = await this.prisma.org_agents.findFirstOrThrow({ - where: { - orgId + async getAgentTypeDetails(): Promise { + try { + const { id } = await this.prisma.agents_type.findFirst({ + where: { + agent: AgentType.AFJ + } + }); + return id; + } catch (error) { + this.logger.error(`[getAgentTypeDetails] - get org agent health details: ${JSON.stringify(error)}`); + throw error; } - }); - return oranizationAgentDetails; - } catch (error) { - this.logger.error(`[getOrgAgentDetails] - get org agent health details: ${JSON.stringify(error)}`); - throw error; } - } - async getAgentTypeDetails(): Promise { - try { - const { id } = await this.prisma.agents_type.findFirst({ - where: { - agent: AgentType.AFJ + async getLedgerDetails(name: string[] | string): Promise { + try { + let whereClause; + + if (Array.isArray(name)) { + whereClause = { + name: { + in: name + } + }; + } else { + whereClause = { + name + }; + } + + const ledgersDetails = await this.prisma.ledgers.findMany({ + where: whereClause, + select: { + id: true + } + }); + return ledgersDetails; + } catch (error) { + this.logger.error(`[getLedgerDetails] - get ledger details: ${JSON.stringify(error)}`); + throw error; } - }); - return id; - } catch (error) { - this.logger.error(`[getAgentTypeDetails] - get org agent health details: ${JSON.stringify(error)}`); - throw error; } - } - - async getLedgerDetails(name: string[] | string): Promise<{ - id: string; - createDateTime: Date; - lastChangedDateTime: Date; - name: string; - networkType: string; - poolConfig: string; - isActive: boolean; - networkString: string; - registerDIDEndpoint: string; - registerDIDPayload: Prisma.JsonValue; - indyNamespace: string; - }[]> { - try { - let whereClause; - - if (Array.isArray(name)) { - whereClause = { - name: { - in: name - } - }; - } else { - whereClause = { - name - }; - } - - const ledgersDetails = await this.prisma.ledgers.findMany({ - where: whereClause - }); - return ledgersDetails; - } catch (error) { - this.logger.error(`[getLedgerDetails] - get ledger details: ${JSON.stringify(error)}`); - throw error; + + async getOrgAgentTypeDetails(agentType: string): Promise { + try { + const { id } = await this.prisma.org_agents_type.findFirstOrThrow({ + where: { + agent: agentType + } + }); + return id; + } catch (error) { + this.logger.error(`[getOrgAgentTypeDetails] - get org agent type details: ${JSON.stringify(error)}`); + throw error; + } } - } - async getOrgAgentTypeDetails(agentType: string): Promise { - try { - const { id } = await this.prisma.org_agents_type.findFirst({ - where: { - agent: agentType + async getPlatfomOrg(orgName: string): Promise { + try { + const { id } = await this.prisma.organisation.findFirst({ + where: { + name: orgName + } + }); + return id; + } catch (error) { + this.logger.error(`[getPlatfomOrg] - get platform org details: ${JSON.stringify(error)}`); + throw error; } - }); - return id; - } catch (error) { - this.logger.error(`[getOrgAgentTypeDetails] - get org agent type details: ${JSON.stringify(error)}`); - throw error; } - } - async getPlatfomOrg(orgName: string): Promise { - try { - const { id } = await this.prisma.organisation.findFirst({ - where: { - name: orgName + async getPlatfomAdminUser(platformAdminUserEmail: string): Promise { + try { + const platformAdminUser = await this.prisma.user.findUnique({ + where: { + email: platformAdminUserEmail + } + }); + return platformAdminUser; + } catch (error) { + this.logger.error(`[getPlatfomAdminUser] - get platform admin user: ${JSON.stringify(error)}`); + throw error; } - }); - return id; - } catch (error) { - this.logger.error(`[getPlatfomOrg] - get platform org details: ${JSON.stringify(error)}`); - throw error; } - } - async getPlatfomAdminUser(platformAdminUserEmail: string): Promise { - try { - const platformAdminUser = await this.prisma.user.findUnique({ - where: { - email: platformAdminUserEmail + async getAgentType(id: string): Promise { + try { + if (id) { + + const { agent } = await this.prisma.agents_type.findUnique({ + where: { + id + } + }); + return agent; + } + } catch (error) { + this.logger.error(`[getAgentType] - get agent type details: ${JSON.stringify(error)}`); + throw error; } - }); - return platformAdminUser; - } catch (error) { - this.logger.error(`[getPlatfomAdminUser] - get platform admin user: ${JSON.stringify(error)}`); - throw error; } - } - async getAgentType(id: string): Promise { - try { - const { agent } = await this.prisma.agents_type.findUnique({ - where: { - id + async getAgentTypeId(agentType: string): Promise { + try { + const { id } = await this.prisma.agents_type.findFirstOrThrow({ + where: { + agent: agentType + } + }); + return id; + } catch (error) { + this.logger.error(`[getAgentType] - get agent type details: ${JSON.stringify(error)}`); + throw error; } - }); - return agent; - } catch (error) { - this.logger.error(`[getAgentType] - get agent type details: ${JSON.stringify(error)}`); - throw error; } - } - async getAgentTypeId(agentType: string): Promise { - try { - const { id } = await this.prisma.agents_type.findFirst({ - where: { - agent: agentType + /** + * Get agent details + * @param orgId + * @returns Agent health details + */ + // eslint-disable-next-line camelcase + async getOrgAgentDetails(orgId: string): Promise { + try { + if (orgId) { + + const oranizationAgentDetails = await this.prisma.org_agents.findUnique({ + where: { + orgId + } + }); + return oranizationAgentDetails; + } + } catch (error) { + this.logger.error(`[getOrgAgentDetails] - get org agent health details: ${JSON.stringify(error)}`); + throw error; } - }); - return id; - } catch (error) { - this.logger.error(`[getAgentType] - get agent type details: ${JSON.stringify(error)}`); - throw error; } - } } \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/agent-service.controller.ts b/apps/api-gateway/src/agent-service/agent-service.controller.ts index c9c0d916e..d68b2e326 100644 --- a/apps/api-gateway/src/agent-service/agent-service.controller.ts +++ b/apps/api-gateway/src/agent-service/agent-service.controller.ts @@ -121,6 +121,14 @@ export class AgentController { return res.status(HttpStatus.CREATED).json(finalResponse); } + /** + * Create wallet for shared agent + * @param orgId + * @param createTenantDto + * @param user + * @param res + * @returns wallet initialization status + */ @Post('/orgs/:orgId/agents/wallet') @ApiOperation({ summary: 'Shared Agent', @@ -134,20 +142,24 @@ export class AgentController { @Body() createTenantDto: CreateTenantDto, @User() user: user, @Res() res: Response - ): Promise { + ): Promise { createTenantDto.orgId = orgId; if (seedLength !== createTenantDto.seed.length) { - throw new BadRequestException(`seed must be at most 32 characters.`); + this.logger.error(`seed must be at most 32 characters`); + throw new BadRequestException( + ResponseMessages.agent.error.seedCharCount, + { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } + ); } const tenantDetails = await this.agentService.createTenant(createTenantDto, user); - const finalResponse: IResponseType = { + const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, message: ResponseMessages.agent.success.create, - data: tenantDetails.response + data: tenantDetails }; return res.status(HttpStatus.CREATED).json(finalResponse); diff --git a/apps/api-gateway/src/agent-service/agent-service.service.ts b/apps/api-gateway/src/agent-service/agent-service.service.ts index 4b49acdea..c36cee8d8 100644 --- a/apps/api-gateway/src/agent-service/agent-service.service.ts +++ b/apps/api-gateway/src/agent-service/agent-service.service.ts @@ -4,6 +4,7 @@ import { user } from '@prisma/client'; import { BaseService } from 'libs/service/base.service'; import { AgentSpinupDto } from './dto/agent-service.dto'; import { CreateTenantDto } from './dto/create-tenant.dto'; +import { AgentSpinUpSatus } from './interface/agent-service.interface'; import { AgentStatus } from './interface/agent-service.interface'; @Injectable() @@ -19,9 +20,11 @@ export class AgentService extends BaseService { return this.sendNats(this.agentServiceProxy, 'agent-spinup', payload); } - async createTenant(createTenantDto: CreateTenantDto, user: user): Promise<{ response: object }> { + async createTenant(createTenantDto: CreateTenantDto, user: user): Promise { const payload = { createTenantDto, user }; - return this.sendNats(this.agentServiceProxy, 'create-tenant', payload); + + // NATS call + return this.sendNatsMessage(this.agentServiceProxy, 'create-tenant', payload); } async getAgentHealth(user: user, orgId:string): Promise { 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 34938eef9..9204d6370 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 @@ -7,7 +7,7 @@ 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 }) => value.trim()) + @Transform(({ value }) => trim(value)) @MinLength(2, { message: 'Minimum length for label must be 2 characters.' }) @Matches(labelRegex, { message: 'Label must not contain special characters.' }) @Matches(/^\S*$/, { @@ -15,7 +15,7 @@ export class CreateTenantDto { }) label: string; - @ApiProperty() + @ApiProperty({ example: 'dfuhgfklskmjngrjekjfgjjfkoekfdad' }) @MaxLength(32, { message: 'seed must be at most 32 characters.' }) @Transform(({ value }) => trim(value)) @IsNotEmpty({ message: 'seed is required' }) @@ -26,20 +26,23 @@ export class CreateTenantDto { seed: string; @ApiProperty({ example: [1] }) + @ApiPropertyOptional() @IsOptional() @IsArray({ message: 'ledgerId must be an array' }) @IsNotEmpty({ message: 'please provide valid ledgerId' }) ledgerId?: string[]; - orgId: string; - - @ApiProperty() + @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) @IsOptional() + @ApiPropertyOptional() @IsString({ message: 'did must be in string format.' }) did?: string; - @ApiProperty() + @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) @IsOptional() @ApiPropertyOptional() + @IsString({ message: 'did must be in string format.' }) clientSocketId?: string; + + orgId: string; } \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts b/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts index 0a5a5e146..6ced1ed42 100644 --- a/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts +++ b/apps/api-gateway/src/agent-service/interface/agent-service.interface.ts @@ -1,3 +1,6 @@ +export interface AgentSpinUpSatus { + agentSpinupStatus: number; +} export interface AgentStatus { label: string; endpoints: string[]; diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 63dfe6fc6..48cd54c5f 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -159,8 +159,13 @@ export const ResponseMessages = { notAbleToSpinUpAgent: 'Agent not able to spin-up', alreadySpinUp: 'Agent already spin-up', agentUrl: 'Agent url not exist', - agentNotExists: 'Agent not spinned up for this organization', - agentDown: 'Agent is down or not spinned up' + agentNotExists: 'Agent not spun up for this organization', + agentDown: 'Agent is down or not spun up', + walletAlreadyCreated: 'Your wallet is already been created', + walletAlreadyProcessing: 'Your wallet is already processing', + notAbleToSpinp: 'Agent not able to spin-up', + platformAdminNotAbleToSpinp: 'Platform-admin agent is not spun up', + seedCharCount: 'seed must be at most 32 characters' } }, connection: { diff --git a/libs/prisma-service/prisma/migrations/20231227105815_org_id_unique_org_agent/migration.sql b/libs/prisma-service/prisma/migrations/20231227105815_org_id_unique_org_agent/migration.sql new file mode 100644 index 000000000..241cb06ea --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20231227105815_org_id_unique_org_agent/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - A unique constraint covering the columns `[orgId]` on the table `org_agents` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateIndex +CREATE UNIQUE INDEX "org_agents_orgId_key" ON "org_agents"("orgId"); diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index d8885aa6e..4e1a47116 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -168,7 +168,7 @@ model org_agents { tenantId String? apiKey String? agentsTypeId String? @db.Uuid - orgId String? @db.Uuid + orgId String? @unique @db.Uuid orgAgentTypeId String? @db.Uuid ledgerId String? @db.Uuid agent_invitations agent_invitations[]