diff --git a/Dockerfiles/Dockerfile.agent-provisioning b/Dockerfiles/Dockerfile.agent-provisioning index 17c820995..f6fada0b5 100644 --- a/Dockerfiles/Dockerfile.agent-provisioning +++ b/Dockerfiles/Dockerfile.agent-provisioning @@ -25,7 +25,7 @@ COPY . . # Generate Prisma client # RUN cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate -RUN cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate +RUN cd libs/prisma-service && npx prisma migrate && npx prisma generate RUN ls -R /app/apps/agent-provisioning/AFJ/ # Build the user service diff --git a/Dockerfiles/Dockerfile.agent-service b/Dockerfiles/Dockerfile.agent-service index 0a6d8d3f8..074b0c07f 100644 --- a/Dockerfiles/Dockerfile.agent-service +++ b/Dockerfiles/Dockerfile.agent-service @@ -21,7 +21,7 @@ RUN pnpm i # Copy the rest of the application code COPY . . # RUN cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate -RUN cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate +RUN cd libs/prisma-service && npx prisma migrate && npx prisma generate # Build the user service RUN pnpm run build agent-service diff --git a/apps/agent-provisioning/AFJ/scripts/on_premises_agent.sh b/apps/agent-provisioning/AFJ/scripts/on_premises_agent.sh new file mode 100644 index 000000000..54084f000 --- /dev/null +++ b/apps/agent-provisioning/AFJ/scripts/on_premises_agent.sh @@ -0,0 +1,341 @@ +#!/bin/bash + +START_TIME=$(date +%s) + +# Check if Docker is installed +if ! command -v docker &> /dev/null; then + echo "Docker is not installed. Installing Docker..." + + # Install Docker + curl -fsSL https://get.docker.com -o get-docker.sh + sudo sh get-docker.sh + + # Add the current user to the docker group + sudo usermod -aG docker $USER + + # Start and enable the Docker service + sudo systemctl start docker + sudo systemctl enable docker + + echo "Docker has been installed." +else + echo "Docker is already installed." +fi + +# Function to prompt user for input +prompt_input() { + local prompt_message=$1 + local input_variable=$2 + read -p "$prompt_message" $input_variable +} + +prompt_input_with_tenant_validation() { + local prompt_message=$1 + local input_variable=$2 + local validation_message=$3 + + while true; do + read -p "$prompt_message" $input_variable + case "${!input_variable}" in + true | false) + break + ;; + *) + echo "$validation_message" + ;; + esac + done +} + +prompt_input_with_webhook_host_validation() { + local prompt_message=$1 + local input_variable=$2 + local validation_message=$3INDY_LEDGER_FORMATTED + + while true; do + read -p "$prompt_message" $input_variable + local input_value="${!input_variable}" + local ip_address=$(echo "$input_value" | cut -d ':' -f 1 | sed 's/http:\/\///;s/https:\/\///') + local port=$(echo "$input_value" | cut -d ':' -f 3) + + if [[ "$input_value" =~ ^http:\/\/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$ && "$port" =~ ^[0-9]+$ ]]; then + break + elif [[ "$input_value" =~ ^https:\/\/[a-zA-Z0-9.-]+$ ]]; then + break + else + echo "$validation_message" + fi + done +} + +# Function to validate INDY_LEDGER input against the provided options +validate_indy_ledger() { + local input_ledger=$1 + case "$input_ledger" in + 1) echo 'No ledger' ;; # Option for "no ledger" + 2) echo 'Polygon' ;; # Option for "polygon" + 3) echo '{"genesisTransactions":"http://test.bcovrin.vonx.io/genesis","indyNamespace":"bcovrin:testnet"}' ;; + 4) echo '{"genesisTransactions":"https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_testnet_genesis","indyNamespace":"indicio:testnet"}' ;; + 5) echo '{"genesisTransactions":"https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_demonet_genesis","indyNamespace":"indicio:demonet"}' ;; + 6) echo '{"genesisTransactions":"https://raw.githubusercontent.com/Indicio-tech/indicio-network/main/genesis_files/pool_transactions_mainnet_genesis","indyNamespace":"indicio:mainnet"}' ;; + *) echo "Invalid choice" ;; + esac +} + +# Prompt user for input +prompt_input "Enter ORGANIZATION_ID: " ORGANIZATION_ID +prompt_input "Enter WALLET_NAME: " WALLET_NAME +prompt_input "Enter WALLET_PASSWORD: " WALLET_PASSWORD + +# Prompt user for RANDOM_SEED input and validate it +while true; do + prompt_input "Enter RANDOM_SEED: " RANDOM_SEED + if [ ${#RANDOM_SEED} -eq 32 ]; then + break + else + echo "Error: RANDOM_SEED must be exactly 32 characters." + fi +done + +echo "RANDOM_SEED accepted: $RANDOM_SEED" + +# Display options to the user for INDY_LEDGER +echo "Choose INDY_LEDGER option(s) (comma-separated, e.g., 1):" +echo "1) No ledger" +echo "2) Polygon" +echo "3) bcovrin:testnet" +echo "4) indicio:testnet" +echo "5) indicio:demonet" +echo "6) indicio:mainnet" + +# Prompt user to choose INDY_LEDGER option(s) +INDY_LEDGER=() +read -r INDY_LEDGER_INPUT + +# Split the input by commas and process each choice +IFS=',' read -ra CHOICES <<<"$INDY_LEDGER_INPUT" +for choice in "${CHOICES[@]}"; do + choice=$(echo "$choice" | tr -d ' ') # Remove any spaces + case $choice in + 1 | 2 | 3 | 4 | 5 | 6) + if [ -n "${INDY_LEDGER}" ]; then + echo "Error: Only one INDY_LEDGER option can be selected." + exit 1 + fi + INDY_LEDGER=$(validate_indy_ledger $choice) + ;; + *) + echo "Invalid choice: $choice" + ;; + esac +done + +# Check if "No ledger" or "Polygon" is selected and set INDY_LEDGER_FORMATTED accordingly +if [ "$INDY_LEDGER" = "No ledger" ] || [ "$INDY_LEDGER" = "Polygon" ]; then + INDY_LEDGER_FORMATTED="[]" +else + # Set INDY_LEDGER_FORMATTED based on selected option + INDY_LEDGER_FORMATTED="[$INDY_LEDGER]" +fi + +echo "INDY_LEDGER chosen: $INDY_LEDGER" +echo "Formatted INDY_LEDGER: $INDY_LEDGER_FORMATTED" + +# Proceed to prompt for other parameters +prompt_input_with_webhook_host_validation "Enter WEBHOOK_HOST (host/domain): " WEBHOOK_HOST "Error: WEBHOOK_HOST must be in the format http://host:port or https://domain." +prompt_input "Enter WALLET_STORAGE_HOST: " WALLET_STORAGE_HOST +prompt_input "Enter WALLET_STORAGE_PORT: " WALLET_STORAGE_PORT +prompt_input "Enter WALLET_STORAGE_USER: " WALLET_STORAGE_USER +prompt_input "Enter WALLET_STORAGE_PASSWORD: " WALLET_STORAGE_PASSWORD +prompt_input "Enter AGENT_NAME: " AGENT_NAME +prompt_input "Enter PROTOCOL: " PROTOCOL +prompt_input_with_tenant_validation "Enter TENANT (true/false): " TENANT "Error: TENANT must be either 'true' or 'false'." +prompt_input "Enter CREDO_IMAGE: " CREDO_IMAGE +prompt_input "Enter INBOUND_ENDPOINT: " INBOUND_ENDPOINT +prompt_input "Enter ADMIN_PORT: " ADMIN_PORT +prompt_input "Enter INBOUND_PORT: " INBOUND_PORT + +# Run the command using user input +on_premises_agent.sh --ORGANIZATION_ID "$ORGANIZATION_ID" --WALLET_NAME "$WALLET_NAME" --WALLET_PASSWORD "$WALLET_PASSWORD" --RANDOM_SEED "$RANDOM_SEED" --WEBHOOK_HOST "$WEBHOOK_HOST" --WALLET_STORAGE_HOST "$WALLET_STORAGE_HOST" --WALLET_STORAGE_PORT "$WALLET_STORAGE_PORT" --WALLET_STORAGE_USER "$WALLET_STORAGE_USER" --WALLET_STORAGE_PASSWORD "$WALLET_STORAGE_PASSWORD" --AGENT_NAME "$AGENT_NAME" --PROTOCOL "$PROTOCOL" --TENANT "$TENANT" --CREDO_IMAGE "$CREDO_IMAGE" --INDY_LEDGER "$INDY_LEDGER" --INBOUND_ENDPOINT "$INBOUND_ENDPOINT" --ADMIN_PORT "$ADMIN_PORT" --INBOUND_PORT "$INBOUND_PORT" + +echo "admin port: $ADMIN_PORT" +echo "inbound port: $INBOUND_PORT" + +echo "AGENT SPIN-UP STARTED" + +if [ -d "${PWD}/agent-config" ]; then + echo "agent-config directory exists." +else + echo "Error: agent-config directory does not exists." + mkdir ${PWD}/agent-config +fi + +# Define a regular expression pattern for IP address +IP_REGEX="^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$" + +# Check if the input is a domain +if echo "$INBOUND_ENDPOINT" | grep -qP "^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"; then + echo "INBOUND_ENDPOINT is a domain: $INBOUND_ENDPOINT" + AGENT_ENDPOINT=$INBOUND_ENDPOINT +else + # Check if the input is an IP address + if [[ $INBOUND_ENDPOINT =~ $IP_REGEX ]]; then + echo "INBOUND_ENDPOINT is an IP address: $INBOUND_ENDPOINT" + AGENT_ENDPOINT="${PROTOCOL}://${INBOUND_ENDPOINT}:${INBOUND_PORT}" + else + echo "Invalid input for INBOUND_ENDPOINT: $INBOUND_ENDPOINT" + fi +fi + +echo "-----$AGENT_ENDPOINT----" +CONFIG_FILE="${PWD}/agent-config/${ORGANIZATION_ID}_${AGENT_NAME}.json" + +# Check if the file exists +if [ -f "$CONFIG_FILE" ]; then + # If it exists, remove the file + rm "$CONFIG_FILE" +fi + +cat <${CONFIG_FILE} +{ + "label": "${ORGANIZATION_ID}_${AGENT_NAME}", + "walletId": "$WALLET_NAME", + "walletKey": "$WALLET_PASSWORD", + "walletType": "postgres", + "walletUrl": "$WALLET_STORAGE_HOST:$WALLET_STORAGE_PORT", + "walletAccount": "$WALLET_STORAGE_USER", + "walletPassword": "$WALLET_STORAGE_PASSWORD", + "walletAdminAccount": "$WALLET_STORAGE_USER", + "walletAdminPassword": "$WALLET_STORAGE_PASSWORD", + "walletScheme": "DatabasePerWallet", + "indyLedger": $INDY_LEDGER_FORMATTED, + "endpoint": [ + "$AGENT_ENDPOINT" + ], + "autoAcceptConnections": true, + "autoAcceptCredentials": "contentApproved", + "autoAcceptProofs": "contentApproved", + "logLevel": 5, + "inboundTransport": [ + { + "transport": "$PROTOCOL", + "port": "$INBOUND_PORT" + } + ], + "outboundTransport": [ + "$PROTOCOL" + ], + "webhookUrl": "$WEBHOOK_HOST/wh/$ORGANIZATION_ID", + "adminPort": "$ADMIN_PORT", + "tenancy": $TENANT +} +EOF + +FILE_NAME="docker-compose_${ORGANIZATION_ID}_${AGENT_NAME}.yaml" + +DOCKER_COMPOSE="${PWD}/${FILE_NAME}" + +# Check if the file exists +if [ -f "$DOCKER_COMPOSE" ]; then + # If it exists, remove the file + rm "$DOCKER_COMPOSE" +fi +cat <${DOCKER_COMPOSE} +version: '3' + +services: + agent: + image: $CREDO_IMAGE + + container_name: ${ORGANIZATION_ID}_${AGENT_NAME} + restart: always + environment: + AFJ_REST_LOG_LEVEL: 1 + ports: + - ${INBOUND_PORT}:${INBOUND_PORT} + - ${ADMIN_PORT}:${ADMIN_PORT} + + volumes: + - ./agent-config/${ORGANIZATION_ID}_${AGENT_NAME}.json:/config.json + + command: --auto-accept-connections --config /config.json + +volumes: + pgdata: + agent-indy_client: + agent-tmp: +EOF + +if [ $? -eq 0 ]; then + cd ${PWD} + echo "docker-compose generated successfully!" + echo "=================" + echo "spinning up the container" + echo "=================" + echo "container-name::::::${AGENT_NAME}" + echo "file-name::::::$FILE_NAME" + + docker compose -p "${ORGANIZATION_ID}_${AGENT_NAME}" -f $FILE_NAME up -d + if [ $? -eq 0 ]; then + + echo "Creating agent config" + # Capture the logs from the container + container_id=$(docker ps -q --filter "name=${ORGANIZATION_ID}_${AGENT_NAME}") + + if [ -z "$container_id" ]; then + echo "Error: No container found with name ${ORGANIZATION_ID}_${AGENT_NAME}" + exit 1 + fi + + # Wait for the container to generate logs + retries=5 + delay=10 + while [ $retries -gt 0 ]; do + container_logs=$(docker logs "$container_id" 2>/dev/null) + if [ -n "$container_logs" ]; then + break + else + echo "Waiting for logs to be generated..." + sleep $delay + retries=$((retries - 1)) + fi + done + + if [ -z "$container_logs" ]; then + echo "Error: No logs found for container ${ORGANIZATION_ID}_${AGENT_NAME} after waiting" + exit 1 + fi + + # Extract the token from the logs using sed + token=$(echo "$container_logs" | sed -nE 's/.*API Token: ([^ ]+).*/\1/p') + + if [ -z "$token" ]; then + echo "Error: Failed to extract API token from logs" + exit 1 + fi + + # Highlight the token line when printing + highlighted_token="Token: \x1b[1;31m$token\x1b[0m" + + # Print the extracted token with highlighting + echo -e "$highlighted_token" + echo "Agent config created" + + # Check if the token exists to determine if the agent is running + if [ -n "$token" ]; then + echo "Agent is running" + else + echo "Agent is not running" + exit 1 + fi + + else + echo "===============" + echo "ERROR : Failed to spin up the agent!" + echo "===============" && exit 125 + fi +else + echo "ERROR : Failed to execute!" && exit 125 +fi + +echo "Total time elapsed: $(date -ud "@$(($(date +%s) - $START_TIME))" +%T) (HH:MM:SS)" diff --git a/apps/agent-provisioning/src/agent-provisioning.service.ts b/apps/agent-provisioning/src/agent-provisioning.service.ts index 58a587dd2..92d382358 100644 --- a/apps/agent-provisioning/src/agent-provisioning.service.ts +++ b/apps/agent-provisioning/src/agent-provisioning.service.ts @@ -25,7 +25,7 @@ export class AgentProvisioningService { const { containerName, externalIp, orgId, seed, walletName, walletPassword, walletStorageHost, walletStoragePassword, walletStoragePort, walletStorageUser, webhookEndpoint, agentType, protocol, credoImage, tenant, indyLedger, inboundEndpoint } = payload; if (agentType === AgentType.AFJ) { // The wallet provision command is used to invoke a shell script - const walletProvision = `${process.cwd() + process.env.AFJ_AGENT_SPIN_UP} ${orgId} "${externalIp}" "${walletName}" "${walletPassword}" ${seed} ${webhookEndpoint} ${walletStorageHost} ${walletStoragePort} ${walletStorageUser} ${walletStoragePassword} ${containerName} ${protocol} ${tenant} ${afjVersion} "${indyLedger}" ${inboundEndpoint} ${process.env.SCHEMA_FILE_SERVER_URL} ${process.env.AGENT_HOST} ${process.env.AWS_ACCOUNT_ID} ${process.env.S3_BUCKET_ARN} ${process.env.CLUSTER_NAME} ${process.env.TESKDEFINITION_FAMILY}`; + const walletProvision = `${process.cwd() + process.env.AFJ_AGENT_SPIN_UP} ${orgId} "${externalIp}" "${walletName}" "${walletPassword}" ${seed} ${webhookEndpoint} ${walletStorageHost} ${walletStoragePort} ${walletStorageUser} ${walletStoragePassword} ${containerName} ${protocol} ${tenant} ${credoImage} "${indyLedger}" ${inboundEndpoint} ${process.env.SCHEMA_FILE_SERVER_URL} ${process.env.AGENT_HOST} ${process.env.AWS_ACCOUNT_ID} ${process.env.S3_BUCKET_ARN} ${process.env.CLUSTER_NAME} ${process.env.TESKDEFINITION_FAMILY}`; const spinUpResponse: object = new Promise(async (resolve) => { await exec(walletProvision, async (err, stdout, stderr) => { diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 79c7864c7..a2e4a4197 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -1,7 +1,28 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { AgentServiceService } from './agent-service.service'; -import { IAgentStatus, IConnectionDetails, IUserRequestInterface, ISendProofRequestPayload, IAgentSpinUpSatus, IGetCredDefAgentRedirection, IGetSchemaAgentRedirection, IAgentSpinupDto, IIssuanceCreateOffer, ITenantCredDef, ITenantDto, ITenantSchema, IOutOfBandCredentialOffer, IAgentProofRequest, IDidCreate, IWallet, ITenantRecord } from './interface/agent-service.interface'; +import { + IAgentStatus, + IConnectionDetails, + IUserRequestInterface, + ISendProofRequestPayload, + IAgentSpinUpSatus, + IGetCredDefAgentRedirection, + IGetSchemaAgentRedirection, + IAgentSpinupDto, + IIssuanceCreateOffer, + ITenantCredDef, + ITenantDto, + ITenantSchema, + IOutOfBandCredentialOffer, + IAgentProofRequest, + IDidCreate, + IWallet, + ITenantRecord, + ICreateConnectionInvitation, + IStoreAgent, + IAgentConfigure +} from './interface/agent-service.interface'; import { user } from '@prisma/client'; import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; import { AgentSpinUpStatus } from '@credebl/enum/enum'; @@ -40,7 +61,7 @@ export class AgentServiceController { } @MessagePattern({ cmd: 'create-wallet' }) - async createWallet(payload: { createWalletDto: IWallet, user: IUserRequestInterface}): Promise { + async createWallet(payload: { createWalletDto: IWallet, user: IUserRequestInterface }): Promise { return this.agentServiceService.createWallet(payload.createWalletDto); } @@ -76,16 +97,24 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-create-connection-legacy-invitation' }) - async createLegacyConnectionInvitation(payload: { connectionPayload: IConnectionDetails, url: string, orgId: string }): Promise { - return this.agentServiceService.createLegacyConnectionInvitation(payload.connectionPayload, payload.url, payload.orgId); + async createLegacyConnectionInvitation(payload: { + connectionPayload: IConnectionDetails; + url: string, + orgId: string, + }): Promise { + return this.agentServiceService.createLegacyConnectionInvitation( + payload.connectionPayload, + payload.url, + payload.orgId + ); } //DONE @MessagePattern({ cmd: 'agent-send-credential-create-offer' }) async sendCredentialCreateOffer(payload: { issueData: IIssuanceCreateOffer; - url: string; - orgId: string; + url: string, + orgId: string, }): Promise { return this.agentServiceService.sendCredentialCreateOffer(payload.issueData, payload.url, payload.orgId); } @@ -98,7 +127,7 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-get-issued-credentials-by-credentialDefinitionId' }) - async getIssueCredentialsbyCredentialRecordId(payload: { url: string; orgId: string }): Promise { + async getIssueCredentialsbyCredentialRecordId(payload: { url: string, orgId: string }): Promise { return this.agentServiceService.getIssueCredentialsbyCredentialRecordId(payload.url, payload.orgId); } //DONE @@ -109,28 +138,28 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-get-proof-presentation-by-id' }) - async getProofPresentationById(payload: { url: string; orgId: string }): Promise { + async getProofPresentationById(payload: { url: string, orgId: string }): Promise { return this.agentServiceService.getProofPresentationById(payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'agent-send-proof-request' }) async sendProofRequest(payload: { - proofRequestPayload: ISendProofRequestPayload; - url: string; - orgId: string; + proofRequestPayload: ISendProofRequestPayload, + url: string, + orgId: string, }): Promise { return this.agentServiceService.sendProofRequest(payload.proofRequestPayload, payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'agent-verify-presentation' }) - async verifyPresentation(payload: { url: string; orgId: string }): Promise { + async verifyPresentation(payload: { url: string, orgId: string }): Promise { return this.agentServiceService.verifyPresentation(payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'agent-get-all-connections' }) - async getConnections(payload: { url: string; orgId: string }): Promise { + async getConnections(payload: { url: string, orgId: string }): Promise { return this.agentServiceService.getConnections(payload.url, payload.orgId); } @@ -157,24 +186,24 @@ export class AgentServiceController { //DONE @MessagePattern({ cmd: 'agent-send-out-of-band-proof-request' }) async sendOutOfBandProofRequest(payload: { - proofRequestPayload: ISendProofRequestPayload; - url: string; - orgId: string; + proofRequestPayload: ISendProofRequestPayload, + url: string, + orgId: string, }): Promise { return this.agentServiceService.sendOutOfBandProofRequest(payload.proofRequestPayload, payload.url, payload.orgId); } //DONE @MessagePattern({ cmd: 'get-agent-verified-proof-details' }) - async getVerifiedProofDetails(payload: { url: string; orgId: string }): Promise { + async getVerifiedProofDetails(payload: { url: string, orgId: string }): Promise { return this.agentServiceService.getVerifiedProofDetails(payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-schema-endorsement-request' }) async schemaEndorsementRequest(payload: { - url: string; - orgId: string; - requestSchemaPayload: object; + url: string, + orgId: string, + requestSchemaPayload: object, }): Promise { return this.agentServiceService.schemaEndorsementRequest(payload.url, payload.orgId, payload.requestSchemaPayload); } @@ -184,62 +213,53 @@ export class AgentServiceController { orgId: string; requestSchemaPayload: object; }): Promise { - return this.agentServiceService.credDefEndorsementRequest( - payload.url, - payload.orgId, - payload.requestSchemaPayload - ); + return this.agentServiceService.credDefEndorsementRequest(payload.url, payload.orgId, payload.requestSchemaPayload); } //DONE @MessagePattern({ cmd: 'agent-sign-transaction' }) - async signTransaction(payload: { url: string; orgId: string; signEndorsementPayload: object }): Promise { + async signTransaction(payload: { url: string, orgId: string, signEndorsementPayload: object }): Promise { return this.agentServiceService.signTransaction(payload.url, payload.orgId, payload.signEndorsementPayload); } //DONE @MessagePattern({ cmd: 'agent-submit-transaction' }) - async submitTransaction(payload: { url: string; orgId: string; submitEndorsementPayload: object }): Promise { + async submitTransaction(payload: { url: string; orgId: string, submitEndorsementPayload: object }): Promise { return this.agentServiceService.sumbitTransaction(payload.url, payload.orgId, payload.submitEndorsementPayload); } //DONE @MessagePattern({ cmd: 'agent-out-of-band-credential-offer' }) - async outOfBandCredentialOffer(payload: { outOfBandIssuancePayload: IOutOfBandCredentialOffer, url: string, orgId: string }): Promise { - return this.agentServiceService.outOfBandCredentialOffer(payload.outOfBandIssuancePayload, payload.url, payload.orgId); + async outOfBandCredentialOffer(payload: { + outOfBandIssuancePayload: IOutOfBandCredentialOffer, + url: string, + orgId: string, + }): Promise { + return this.agentServiceService.outOfBandCredentialOffer( + payload.outOfBandIssuancePayload, + payload.url, + payload.orgId + ); } //DONE @MessagePattern({ cmd: 'delete-wallet' }) - async deleteWallet(payload: { orgId, user }): Promise { - return this.agentServiceService.deleteWallet(payload.orgId, payload.user); + async deleteWallet(payload: { url, apiKey }): Promise { + return this.agentServiceService.deleteWallet(payload.url, payload.apiKey); } @MessagePattern({ cmd: 'agent-receive-invitation-url' }) - async receiveInvitationUrl(payload: { - url, - orgId, - receiveInvitationUrl - }): Promise { + async receiveInvitationUrl(payload: { url, orgId, receiveInvitationUrl }): Promise { return this.agentServiceService.receiveInvitationUrl(payload.receiveInvitationUrl, payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-receive-invitation' }) - async receiveInvitation(payload: { - url, - orgId, - receiveInvitation - }): Promise { + async receiveInvitation(payload: { url, orgId, receiveInvitation }): Promise { return this.agentServiceService.receiveInvitation(payload.receiveInvitation, payload.url, payload.orgId); } @MessagePattern({ cmd: 'agent-send-question' }) - async sendQuestion(payload: { - url, - orgId, - questionPayload - }): Promise { - + async sendQuestion(payload: { url, orgId, questionPayload }): Promise { return this.agentServiceService.sendQuestion(payload.questionPayload, payload.url, payload.orgId); } @@ -254,8 +274,29 @@ export class AgentServiceController { } @MessagePattern({ cmd: 'polygon-create-keys' }) - async createSecp256k1KeyPair(payload: {orgId: string}): Promise { + async createSecp256k1KeyPair(payload: { orgId: string }): Promise { return this.agentServiceService.createSecp256k1KeyPair(payload.orgId); } + @MessagePattern({ cmd: 'agent-create-connection-invitation' }) + async createConnectionInvitation(payload: { + url: string, + orgId: string, + connectionPayload: ICreateConnectionInvitation, + }): Promise { + return this.agentServiceService.createConnectionInvitation(payload.url, payload.orgId, payload.connectionPayload); + } + + /** + * Configure the agent by organization + * @param payload + * @returns Get agent status + */ + @MessagePattern({ cmd: 'agent-configure' }) + async agentConfigure(payload: { + agentConfigureDto: IAgentConfigure, + user: IUserRequestInterface, + }): Promise { + return this.agentServiceService.agentConfigure(payload.agentConfigureDto, payload.user); + } } diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 6c032b460..8d0e852fe 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -52,9 +52,7 @@ import { IStoreAgent, AgentHealthData, IAgentStore, - IAgentConfigure, - OrgDid, - IBasicMessage + IAgentConfigure } from './interface/agent-service.interface'; import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType, PromiseResult } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; @@ -128,7 +126,7 @@ export class AgentServiceService { this.agentServiceRepository.getPlatformConfigDetails(), this.agentServiceRepository.getAgentTypeDetails(), this.agentServiceRepository.getLedgerDetails( - agentSpinupDto.ledgerName ? agentSpinupDto.ledgerName : [Ledgers.Indicio_Demonet] + agentSpinupDto.network ? agentSpinupDto.network : [Ledgers.Indicio_Demonet] ) ]); @@ -234,7 +232,7 @@ export class AgentServiceService { async agentConfigure(agentConfigureDto: IAgentConfigure, user: IUserRequestInterface): Promise { try { - const { agentEndpoint, apiKey, did, walletName, orgId } = agentConfigureDto; + const { agentEndpoint, apiKey, did, walletName, orgId, network } = agentConfigureDto; const { id: userId } = user; const orgExist = await this.agentServiceRepository.getAgentDetails(orgId); if (orgExist) { @@ -276,6 +274,7 @@ export class AgentServiceService { return fulfilledValues.filter((value) => value !== null); }); + const ledgerIdData = await this.agentServiceRepository.getLedgerDetails(network); const getOrganization = await this.agentServiceRepository.getOrgDetails(orgId); const storeAgentConfig = await this.agentServiceRepository.storeOrgAgentDetails({ @@ -287,6 +286,7 @@ export class AgentServiceService { orgId, agentEndPoint: agentEndpoint, orgAgentTypeId, + ledgerId: ledgerIdData.map((ledger) => ledger?.id), apiKey: encryptedToken, userId }); @@ -555,7 +555,7 @@ export class AgentServiceService { const getOrganization = await this.agentServiceRepository.getOrgDetails(orgData?.id); - await this._createLegacyConnectionInvitation(orgData?.id, user, getOrganization.name); + await this._createConnectionInvitation(orgData?.id, user, getOrganization.name); if (agentSpinupDto.clientSocketId) { socket.emit('invitation-url-creation-success', { clientId: agentSpinupDto.clientSocketId }); } @@ -870,12 +870,23 @@ export class AgentServiceService { this.notifyClientSocket('agent-spinup-process-completed', payload.clientSocketId); - await this.agentServiceRepository.storeOrgAgentDetails(storeOrgAgentData); + 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._createLegacyConnectionInvitation(payload.orgId, user, getOrganization.name); + await this._createConnectionInvitation(payload.orgId, user, getOrganization.name); this.notifyClientSocket('invitation-url-creation-success', payload.clientSocketId); } catch (error) { @@ -925,10 +936,13 @@ export class AgentServiceService { * @param payload * @returns did and didDocument */ - async createDid(payload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise { + async createDid(createDidPayload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise { try { + const { isPrimaryDid } = createDidPayload; const agentDetails = await this.agentServiceRepository.getOrgAgentDetails(orgId); - + if (createDidPayload.method === DidMethod.POLYGON) { + createDidPayload.endpoint = agentDetails.agentEndPoint; + } const getApiKey = await this.getOrgAgentApiKey(orgId); const getOrgAgentType = await this.agentServiceRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); let url; @@ -937,8 +951,38 @@ export class AgentServiceService { } else if (getOrgAgentType.agent === OrgAgentType.SHARED) { url = `${agentDetails.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${agentDetails.tenantId}`; } - const didDetails = await this.commonService.httpPost(url, payload, { headers: { authorization: getApiKey } }); - return didDetails; + + delete createDidPayload.isPrimaryDid; + + const didDetails = await this.commonService.httpPost(url, createDidPayload, { headers: { authorization: getApiKey } }); + + if (!didDetails || Object.keys(didDetails).length === 0) { + throw new InternalServerErrorException(ResponseMessages.agent.error.createDid, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); + } + const createdDidDetails = { + orgId, + did: didDetails.did, + didDocument: didDetails.didDocument || didDetails.didDoc, + isPrimaryDid, + orgAgentId: agentDetails.id, + userId: user.id + }; + const storeDidDetails = await this.agentServiceRepository.storeDidDetails(createdDidDetails); + + if (!storeDidDetails) { + throw new InternalServerErrorException(ResponseMessages.agent.error.storeDid, { + cause: new Error(), + description: ResponseMessages.errorMessages.serverError + }); + } + if (isPrimaryDid && storeDidDetails.did) { + await this.agentServiceRepository.setPrimaryDid(storeDidDetails.did, orgId); + } + + return storeDidDetails; } catch (error) { this.logger.error(`error in create did : ${JSON.stringify(error)}`); @@ -1364,9 +1408,7 @@ export class AgentServiceService { '#', `${payload.schemaId}` )}`; - schemaResponse = await this.commonService - .httpGet(url, { headers: { authorization: getApiKey } }) - .then(async (schema) => schema); + schemaResponse = await this.commonService.httpGet(url, { headers: { authorization: getApiKey } }).then(async (schema) => schema); } else if (OrgAgentType.SHARED === payload.agentType) { const url = `${payload.agentEndPoint}${CommonConstants.URL_SHAGENT_GET_SCHEMA}` .replace('@', `${payload.payload.schemaId}`) @@ -1861,11 +1903,10 @@ export class AgentServiceService { const decryptedToken = await this.commonService.decryptPassword(apiKey); return decryptedToken; } catch (error) { - this.logger.error(`Agent api key details : ${JSON.stringify(error)}`); - throw error; + this.logger.error(`Agent api key details : ${JSON.stringify(error)}`); + throw error; } -} - + } async handleAgentSpinupStatusErrors(error: string): Promise { if (error && Object.keys(error).length === 0) { @@ -1965,19 +2006,17 @@ export class AgentServiceService { try { return this.agentServiceProxy .send(pattern, payload) - .pipe( - map((response) => ( - { - response - })) - ).toPromise() - .catch(error => { + .pipe(map((response) => ({ response }))) + .toPromise() + .catch((error) => { this.logger.error(`catch: ${JSON.stringify(error)}`); throw new HttpException( { status: error.statusCode, error: error.message - }, error.error); + }, + error.error + ); }); } catch (error) { this.logger.error(`[natsCall] - error in nats call : ${JSON.stringify(error)}`); diff --git a/apps/agent-service/src/interface/agent-service.interface.ts b/apps/agent-service/src/interface/agent-service.interface.ts index 90b987e65..72a731a3b 100644 --- a/apps/agent-service/src/interface/agent-service.interface.ts +++ b/apps/agent-service/src/interface/agent-service.interface.ts @@ -1,5 +1,4 @@ import { AgentSpinUpStatus } from '@credebl/enum/enum'; -import { Prisma } from '@prisma/client'; import { UserRoleOrgPermsDto } from 'apps/api-gateway/src/dtos/user-role-org-perms.dto'; export interface IAgentSpinupDto { @@ -38,6 +37,7 @@ export interface IAgentConfigure { apiKey: string; orgId: string; network?: string; + tenant?: boolean; } export interface IOutOfBandCredentialOffer { @@ -80,16 +80,17 @@ export interface IWallet { } export interface IDidCreate { - keyType: KeyType; - seed: string; - domain?: string; - network?: string; - privatekey?: string; - endpoint?: string; - method: string; - did?: string; - role?: string; - endorserDid?: string; + keyType: KeyType; + seed: string; + domain?: string; + network?: string; + privatekey?: string; + endpoint?: string; + method: string; + did?: string; + role?: string; + endorserDid?: string; + isPrimaryDid?: boolean; } export interface ITenantSchema { tenantId?: string; @@ -196,6 +197,16 @@ export interface IStoreOrgAgentDetails { tenantId?: string; ledgerId?: string[]; agentType?: string; + userId?: string; +} + +export interface IStoreDidDetails { + orgId: string; + isPrimaryDid?: boolean; + did: string; + didDocument?: string; + userId: string; + orgAgentId: string; } export interface IStoreOrgAgent { @@ -611,45 +622,3 @@ export interface IAgentStore { didDoc?: string; tenantId?: string; } - -export interface LedgerNameSpace { - id: string; - createDateTime: Date; - lastChangedDateTime: Date; - name: string; - networkType: string; - poolConfig: string; - isActive: boolean; - networkString: string; - nymTxnEndpoint: string; - indyNamespace: string; - networkUrl: string; -} - -export interface OrgDid { - id: string; - createDateTime: Date; - createdBy: string; - lastChangedDateTime: Date; - lastChangedBy: string; - orgId: string; - isPrimaryDid: boolean; - did: string; - didDocument: Prisma.JsonValue; - orgAgentId: string; -} - -export interface ILedgers { - id: string; - createDateTime: Date; - lastChangedDateTime: Date; - name: string; - networkType: string; - poolConfig: string; - isActive: boolean; - networkString: string; - nymTxnEndpoint: string; - indyNamespace: string; - networkUrl: 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 d1ae2fc5f..b534f9c89 100644 --- a/apps/agent-service/src/repositories/agent-service.repository.ts +++ b/apps/agent-service/src/repositories/agent-service.repository.ts @@ -1,9 +1,9 @@ import { PrismaService } from '@credebl/prisma-service'; import { ConflictException, Injectable, Logger } from '@nestjs/common'; // eslint-disable-next-line camelcase -import { Prisma, ledgerConfig, ledgers, org_agents, org_agents_type, org_dids, organisation, 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 { ledgerConfig, ledgers, org_agents, org_agents_type, org_dids, organisation, platform_config, user } from '@prisma/client'; +import { ICreateOrgAgent, IOrgAgent, IOrgAgentsResponse, IOrgLedgers, IStoreAgent, IStoreDidDetails, IStoreOrgAgentDetails } from '../interface/agent-service.interface'; +import { AgentType } from '@credebl/enum/enum'; @Injectable() export class AgentServiceRepository { @@ -152,14 +152,77 @@ export class AgentServiceRepository { */ // eslint-disable-next-line camelcase async setPrimaryDid(orgDid: string, orgId: string, didDocument: Prisma.JsonValue): Promise { + try { + const { id, userId, ledgerId, did, didDoc, ...commonFields } = storeOrgAgentDetails; + const firstLedgerId = Array.isArray(ledgerId) ? ledgerId[0] : null; + const data = { + ...commonFields, + ledgerId: firstLedgerId, + createdBy: userId, + lastChangedBy: userId, + didDocument: didDoc, + orgDid: did + }; + + // eslint-disable-next-line camelcase + const query: Promise = id ? + this.prisma.org_agents.update({ + where: { id }, + data + }) : + this.prisma.org_agents.create({ data }); + + return { id: (await query).id }; + } catch (error) { + this.logger.error(`[storeAgentDetails] - store agent details: ${JSON.stringify(error)}`); + throw error; + } + } + + /** + * Store DID details + * @param storeDidDetails + * @returns did details + */ + // eslint-disable-next-line camelcase + async storeDidDetails(storeDidDetails: IStoreDidDetails): Promise { + try { + const {orgId, did, didDocument, isPrimaryDid, userId, orgAgentId} = storeDidDetails; + + return this.prisma.org_dids.create({ + data: { + orgId, + did, + didDocument, + isPrimaryDid, + createdBy: userId, + lastChangedBy: userId, + orgAgentId + } + }); + + } catch (error) { + this.logger.error(`[storeDidDetails] - Store DID details: ${JSON.stringify(error)}`); + throw error; + } + } + } + + + /** + * Set primary DID + * @param did + * @returns did details + */ + // eslint-disable-next-line camelcase + async setPrimaryDid(isPrimaryDid:string, orgId:string): Promise { try { return await this.prisma.org_agents.update({ where: { orgId }, data: { - orgDid, - didDocument + orgDid: isPrimaryDid } }); @@ -168,8 +231,13 @@ export class AgentServiceRepository { throw error; } } - } + + /** + * Get agent details + * @param orgId + * @returns + */ // eslint-disable-next-line camelcase async updateLedgerId(orgId: string, ledgerId: string): Promise { try { 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 c238a5903..5d3ee742b 100644 --- a/apps/api-gateway/src/agent-service/agent-service.controller.ts +++ b/apps/api-gateway/src/agent-service/agent-service.controller.ts @@ -41,11 +41,10 @@ import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler import { Roles } from '../authz/decorators/roles.decorator'; import { OrgRoles } from 'libs/org-roles/enums'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; -import { CreateDidDto } from './dto/create-did.dto'; import { validateDid } from '@credebl/common/did.validator'; import { CreateWalletDto } from './dto/create-wallet.dto'; import { CreateNewDidDto } from './dto/create-new-did.dto'; -import { AgentSpinupValidator, TrimStringParamPipe } from '@credebl/common/cast.helper'; +import { AgentSpinupValidator } from '@credebl/common/cast.helper'; import { AgentConfigureDto } from './dto/agent-configure.dto'; const seedLength = 32; @@ -103,29 +102,6 @@ export class AgentController { return res.status(HttpStatus.OK).json(finalResponse); } - @Get('/orgs/agents/ledgerConfig') - @ApiOperation({ - summary: 'Get the ledger config details', - description: 'Get the ledger config details' - }) - @UseGuards(AuthGuard('jwt')) - async getLedgerDetails( - @User() reqUser: user, - @Res() res: Response - ): Promise { - - const ledgerConfigData = await this.agentService.getLedgerConfig(reqUser); - - const finalResponse: IResponse = { - statusCode: HttpStatus.OK, - message: ResponseMessages.agent.success.ledgerConfig, - data: ledgerConfigData - }; - - return res.status(HttpStatus.OK).json(finalResponse); - - } - /** * Spinup the agent by organization * @param agentSpinupDto @@ -146,17 +122,7 @@ export class AgentController { @User() user: user, @Res() res: Response ): Promise { - - const regex = new RegExp('^[a-zA-Z0-9]+$'); - - if (!regex.test(agentSpinupDto.walletName)) { - this.logger.error(`Please enter valid wallet name, It allows only alphanumeric values`); - throw new BadRequestException( - ResponseMessages.agent.error.seedChar, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); - } - + AgentSpinupValidator.validate(agentSpinupDto); this.logger.log(`**** Spin up the agent...${JSON.stringify(agentSpinupDto)}`); agentSpinupDto.orgId = orgId; @@ -208,81 +174,79 @@ export class AgentController { /** * Create wallet - * @param orgId + * @param orgId * @returns wallet */ - @Post('/orgs/:orgId/agents/createWallet') - @ApiOperation({ - summary: 'Create wallet', - description: 'Create wallet' - }) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) - @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) - async createWallet( - @Param('orgId') orgId: string, - @Body() createWalletDto: CreateWalletDto, - @User() user: user, - @Res() res: Response - ): Promise { - - createWalletDto.orgId = orgId; - const walletDetails = await this.agentService.createWallet(createWalletDto, user); - - const finalResponse: IResponse = { - statusCode: HttpStatus.CREATED, - message: ResponseMessages.agent.success.createWallet, - data: walletDetails - }; - - return res.status(HttpStatus.CREATED).json(finalResponse); - } - + @Post('/orgs/:orgId/agents/createWallet') + @ApiOperation({ + summary: 'Create wallet', + description: 'Create wallet' + }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createWallet( + @Param('orgId') orgId: string, + @Body() createWalletDto: CreateWalletDto, + @User() user: user, + @Res() res: Response + ): Promise { + createWalletDto.orgId = orgId; + const walletDetails = await this.agentService.createWallet(createWalletDto, user); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.agent.success.createWallet, + data: walletDetails + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + } + // This function will be used after multiple did method implementation in create wallet - /** + /** * Create did - * @param orgId + * @param orgId * @returns did */ - @Post('/orgs/:orgId/agents/createDid') - @ApiOperation({ - summary: 'Create did', - description: 'Create did' - }) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) - @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) - async createDid( - @Param('orgId') orgId: string, - @Body() createDidDto: CreateDidDto, - @User() user: user, - @Res() res: Response - ): Promise { - + @Post('/orgs/:orgId/agents/did') + @ApiOperation({ + summary: 'Create new did', + description: 'Create new did for an organization' + }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createDid( + @Param('orgId') orgId: string, + @Body() createDidDto: CreateNewDidDto, + @User() user: user, + @Res() res: Response + ): Promise { await validateDid(createDidDto); if (createDidDto.seed && seedLength !== createDidDto.seed.length) { this.logger.error(`seed must be at most 32 characters.`); - throw new BadRequestException( - ResponseMessages.agent.error.seedChar, - { cause: new Error(), description: ResponseMessages.errorMessages.badRequest } - ); + throw new BadRequestException(ResponseMessages.agent.error.seedChar, { + cause: new Error(), + description: ResponseMessages.errorMessages.badRequest + }); } - const didDetails = await this.agentService.createDid(createDidDto, orgId, user); - - const finalResponse: IResponse = { - statusCode: HttpStatus.CREATED, - message: ResponseMessages.agent.success.createDid, - data: didDetails - }; - - return res.status(HttpStatus.CREATED).json(finalResponse); - } + const didDetails = await this.agentService.createDid(createDidDto, orgId, user); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.agent.success.createDid, + data: didDetails + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + } - /** + /** * Create Secp256k1 key pair for polygon DID - * @param orgId + * @param orgId * @returns Secp256k1 key pair for polygon DID */ @Post('/orgs/:orgId/agents/polygon/create-keys') @@ -315,7 +279,7 @@ export class AgentController { @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) - async agentConfigure( + async agentconfigure( @Param('orgId') orgId: string, @Body() agentConfigureDto: AgentConfigureDto, @User() user: user, @@ -334,27 +298,4 @@ export class AgentController { return res.status(HttpStatus.CREATED).json(finalResponse); } - - @Delete('/orgs/:orgId/agents/wallet') - @ApiOperation({ - summary: 'Delete wallet', - description: 'Delete agent wallet by organization.' - }) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER) - @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) - async deleteWallet( - @Param('orgId', TrimStringParamPipe, new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); }})) orgId: string, - @User() user: user, - @Res() res: Response - ): Promise { - await this.agentService.deleteWallet(orgId, user); - - const finalResponse: IResponseType = { - statusCode: HttpStatus.OK, - message: ResponseMessages.agent.success.walletDelete - }; - - return res.status(HttpStatus.OK).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 e0403b2b7..38efd54e8 100644 --- a/apps/api-gateway/src/agent-service/agent-service.service.ts +++ b/apps/api-gateway/src/agent-service/agent-service.service.ts @@ -8,6 +8,7 @@ import { AgentSpinUpSatus, IWalletRecord } from './interface/agent-service.inter import { AgentStatus } from './interface/agent-service.interface'; import { CreateDidDto } from './dto/create-did.dto'; import { CreateWalletDto } from './dto/create-wallet.dto'; +import { AgentConfigureDto } from './dto/agent-configure.dto'; @Injectable() export class AgentService extends BaseService { @@ -79,11 +80,4 @@ export class AgentService extends BaseService { return this.sendNatsMessage(this.agentServiceProxy, 'agent-configure', payload); } - async deleteWallet(orgId: string, user: user): Promise { - const payload = { orgId, user }; - // NATS call - - return this.sendNatsMessage(this.agentServiceProxy, 'delete-wallet', payload); - } - } \ No newline at end of file diff --git a/apps/api-gateway/src/agent-service/dto/agent-configure.dto.ts b/apps/api-gateway/src/agent-service/dto/agent-configure.dto.ts index a9ae783c5..3663e7755 100644 --- a/apps/api-gateway/src/agent-service/dto/agent-configure.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/agent-configure.dto.ts @@ -1,7 +1,7 @@ -import { IsHostPortOrDomain, trim } from '@credebl/common/cast.helper'; -import { ApiProperty } from '@nestjs/swagger'; +import { trim } from '@credebl/common/cast.helper'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsNotEmpty, IsString, Matches, MaxLength, MinLength } from 'class-validator'; +import { IsBoolean, IsNotEmpty, IsOptional, IsString, Matches, MaxLength, MinLength } from 'class-validator'; const regex = /^[a-zA-Z0-9 ]*$/; export class AgentConfigureDto { @ApiProperty() @@ -15,10 +15,14 @@ export class AgentConfigureDto { }) walletName: string; + @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) + @IsString({ message: 'did must be in string format.' }) + @IsNotEmpty() + did: string; + @ApiProperty({ example: 'https://example.com' }) @IsString() @IsNotEmpty() - @IsHostPortOrDomain() agentEndpoint: string; @ApiProperty() @@ -26,5 +30,15 @@ export class AgentConfigureDto { @IsNotEmpty() apiKey: string; + @ApiPropertyOptional() + @IsString() + @IsOptional() + network?: string; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() + tenant?: boolean; + orgId: string; } diff --git a/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts b/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts index 93f022b6f..d04cce6a9 100644 --- a/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/agent-service.dto.ts @@ -5,40 +5,38 @@ import { IsBoolean, IsNotEmpty, IsOptional, IsString, Matches, MaxLength, MinLen import { CreateDidDto } from './create-did.dto'; const regex = /^[a-zA-Z0-9 ]*$/; export class AgentSpinupDto extends CreateDidDto { + @ApiProperty() + @MaxLength(25, { message: 'Maximum length for wallet must be 25 characters.' }) + @IsString({ message: 'label must be in string format.' }) + @Transform(({ value }) => trim(value)) + @MinLength(2, { message: 'Minimum length for wallet name must be 2 characters.' }) + @Matches(regex, { message: 'Wallet name must not contain special characters.' }) + @Matches(/^\S*$/, { + message: 'Spaces are not allowed in walletName' + }) + walletName: string; - @ApiProperty() - @MaxLength(25, { message: 'Maximum length for wallet must be 25 characters.' }) - @IsString({ message: 'label must be in string format.' }) - @Transform(({ value }) => trim(value)) - @MinLength(2, { message: 'Minimum length for wallet name must be 2 characters.' }) - @Matches(regex, { message: 'Wallet name must not contain special characters.' }) - @Matches(/^\S*$/, { - message: 'Spaces are not allowed in label' - }) - walletName: string; + @ApiProperty() + @Transform(({ value }) => trim(value)) + @IsOptional() + @IsString({ message: 'walletPassword must be in string format.' }) + @IsNotEmpty({ message: 'Password is required.' }) + walletPassword?: string; - @ApiProperty() - @Transform(({ value }) => trim(value)) - @IsString({ message: 'walletPassword must be in string format.' }) - @IsNotEmpty({ message: 'Password is required.' }) - walletPassword: string; + @ApiPropertyOptional({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) + @IsOptional() + @Transform(({ value }) => trim(value)) + @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; + @ApiPropertyOptional({ example: 'ojIckSD2jqNzOqIrAGzL' }) + @IsOptional() + clientSocketId?: string; - @ApiProperty({ example: 'ojIckSD2jqNzOqIrAGzL' }) - @IsOptional() - @ApiPropertyOptional() - clientSocketId?: string; - - @ApiProperty({ example: true }) - @IsOptional() - @IsBoolean() - @ApiPropertyOptional() - tenant?: boolean; - - orgId: string; + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() + tenant?: boolean; + + orgId: string; } diff --git a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts index 3e8deb560..36d14666f 100644 --- a/apps/api-gateway/src/agent-service/dto/create-did.dto.ts +++ b/apps/api-gateway/src/agent-service/dto/create-did.dto.ts @@ -26,45 +26,43 @@ export class CreateDidDto { @IsString({ message: 'method must be in string format.' }) method: string; - @ApiProperty({example: 'bcovrin:testnet'}) + @ApiPropertyOptional({example: 'bcovrin:testnet'}) @IsOptional() - @ApiPropertyOptional() + @Transform(({ value }) => trim(value)) @IsString({ message: 'network must be in string format.' }) network?: string; - @ApiProperty({example: 'www.github.com'}) + @ApiPropertyOptional({example: 'www.github.com'}) @IsOptional() - @ApiPropertyOptional() + @Transform(({ value }) => trim(value)) @IsString({ message: 'domain must be in string format.' }) domain?: string; - @ApiProperty({example: 'endorser'}) + @ApiPropertyOptional({example: 'endorser'}) @IsOptional() - @ApiPropertyOptional() + @Transform(({ value }) => trim(value)) @IsString({ message: 'role must be in string format.' }) role?: string; - @ApiProperty({example: ''}) + @ApiPropertyOptional({example: ''}) @IsOptional() - @ApiPropertyOptional() @IsString({ message: 'private key must be in string format.' }) privatekey?: string; - @ApiProperty({example: 'http://localhost:6006/docs'}) + @ApiPropertyOptional({example: 'http://localhost:6006/docs'}) @IsOptional() - @ApiPropertyOptional() @IsString({ message: 'endpoint must be in string format.' }) endpoint?: string; - @ApiProperty({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) + @ApiPropertyOptional({ example: 'XzFjo1RTZ2h9UVFCnPUyaQ' }) @IsOptional() - @ApiPropertyOptional() + @Transform(({ value }) => trim(value)) @IsString({ message: 'did must be in string format.' }) did?: string; - @ApiProperty({example: 'did:indy:bcovrin:testnet:UEeW111G1tYo1nEkPwMcF'}) + @ApiPropertyOptional({example: 'did:indy:bcovrin:testnet:UEeW111G1tYo1nEkPwMcF'}) @IsOptional() - @ApiPropertyOptional() + @Transform(({ value }) => trim(value)) @IsString({ message: 'endorser did must be in string format.' }) endorserDid?: string; } \ No newline at end of file diff --git a/apps/api-gateway/src/connection/connection.controller.ts b/apps/api-gateway/src/connection/connection.controller.ts index a5bc47f5f..58ac24f68 100644 --- a/apps/api-gateway/src/connection/connection.controller.ts +++ b/apps/api-gateway/src/connection/connection.controller.ts @@ -1,6 +1,6 @@ import IResponseType, {IResponse} from '@credebl/common/interfaces/response.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; -import { Controller, Post, Logger, Body, UseGuards, HttpStatus, Res, Get, Param, UseFilters, Query, Inject, ParseUUIDPipe, BadRequestException, Delete } from '@nestjs/common'; +import { Controller, Post, Logger, Body, UseGuards, HttpStatus, Res, Get, Param, UseFilters, Query, Inject, ParseUUIDPipe, BadRequestException } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { ApiBearerAuth, ApiExcludeEndpoint, ApiForbiddenResponse, ApiOperation, ApiQuery, ApiResponse, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; import { User } from '../authz/decorators/user.decorator'; diff --git a/apps/api-gateway/src/connection/connection.service.ts b/apps/api-gateway/src/connection/connection.service.ts index 6f01533b0..bb7877a80 100644 --- a/apps/api-gateway/src/connection/connection.service.ts +++ b/apps/api-gateway/src/connection/connection.service.ts @@ -4,7 +4,7 @@ import { ClientProxy, RpcException } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { ConnectionDto, CreateOutOfBandConnectionInvitation, ReceiveInvitationDto, ReceiveInvitationUrlDto } from './dtos/connection.dto'; import { IReceiveInvitationRes, IUserRequestInterface } from './interfaces'; -import { IConnectionList, IDeletedConnectionsRecord } from '@credebl/common/interfaces/connection.interface'; +import { IConnectionList } from '@credebl/common/interfaces/connection.interface'; import { AgentConnectionSearchCriteria, IConnectionDetailsById, IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface'; import { BasicMessageDto, QuestionDto } from './dtos/question-answer.dto'; import { user } from '@prisma/client'; @@ -24,16 +24,6 @@ export class ConnectionService extends BaseService { } } - sendBasicMessage( - basicMessageDto: BasicMessageDto - ): Promise { - try { - return this.sendNatsMessage(this.connectionServiceProxy, 'send-basic-message-on-connection', basicMessageDto); - } catch (error) { - throw new RpcException(error.response); - } - } - getConnectionWebhook( connectionDto: ConnectionDto, orgId: string diff --git a/apps/api-gateway/src/connection/dtos/connection.dto.ts b/apps/api-gateway/src/connection/dtos/connection.dto.ts index 896c201dd..d06fd06c9 100644 --- a/apps/api-gateway/src/connection/dtos/connection.dto.ts +++ b/apps/api-gateway/src/connection/dtos/connection.dto.ts @@ -138,6 +138,12 @@ export class CreateConnectionDto { @IsOptional() @IsNotEmpty({ message: 'Please provide recipientKey' }) recipientKey: string; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + @IsNotEmpty({ message: 'Please provide invitation did' }) + invitationDid?: string; } export class ConnectionDto { diff --git a/apps/api-gateway/src/credential-definition/credential-definition.controller.ts b/apps/api-gateway/src/credential-definition/credential-definition.controller.ts index 8ca949b27..17ad88444 100644 --- a/apps/api-gateway/src/credential-definition/credential-definition.controller.ts +++ b/apps/api-gateway/src/credential-definition/credential-definition.controller.ts @@ -99,6 +99,27 @@ export class CredentialDefinitionController { return res.status(HttpStatus.OK).json(credDefResponse); } + @Get('/orgs/:orgId/bulk/cred-defs') + @ApiOperation({ + summary: 'Fetch all credential definitions for bulk opeartion', + description: 'Retrieve all credential definitions for bulk operation' + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + async getAllCredDefAndSchemaForBulkOperation( + @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); }})) orgId: string, + @Res() res: Response + ): Promise { + const credentialsDefinitionDetails = await this.credentialDefinitionService.getAllCredDefAndSchemaForBulkOperation(orgId); + const credDefResponse: IResponse = { + statusCode: HttpStatus.OK, + message: ResponseMessages.credentialDefinition.success.fetch, + data: credentialsDefinitionDetails + }; + return res.status(HttpStatus.CREATED).json(credDefResponse); + } + @Post('/orgs/:orgId/cred-defs') @ApiOperation({ summary: 'Sends a credential definition to ledger', diff --git a/apps/api-gateway/src/credential-definition/credential-definition.service.ts b/apps/api-gateway/src/credential-definition/credential-definition.service.ts index ccf9981d2..296112917 100644 --- a/apps/api-gateway/src/credential-definition/credential-definition.service.ts +++ b/apps/api-gateway/src/credential-definition/credential-definition.service.ts @@ -4,7 +4,7 @@ import { CreateCredentialDefinitionDto } from './dto/create-cred-defs.dto'; import { BaseService } from '../../../../libs/service/base.service'; import { IUserRequestInterface } from '../interfaces/IUserRequestInterface'; import { GetAllCredDefsDto } from '../dtos/get-cred-defs.dto'; -import { ICredDef, ICredDefs } from './interfaces'; +import { ICredDef, ICredDefs, ICredentialDefinition } from './interfaces'; import { ICredDefData } from '@credebl/common/interfaces/cred-def.interface'; @Injectable() @@ -35,4 +35,9 @@ export class CredentialDefinitionService extends BaseService { const payload = { schemaId }; return this.sendNatsMessage(this.credDefServiceProxy, 'get-all-credential-definitions-by-schema-id', payload); } + + getAllCredDefAndSchemaForBulkOperation(orgId:string): Promise { + const payload = { orgId }; + return this.sendNatsMessage(this.credDefServiceProxy, 'get-all-schema-cred-defs-for-bulk-operation', payload); + } } diff --git a/apps/api-gateway/src/ecosystem/dtos/add-organizations.dto.ts b/apps/api-gateway/src/ecosystem/dtos/add-organizations.dto.ts index bab205035..717dba3aa 100644 --- a/apps/api-gateway/src/ecosystem/dtos/add-organizations.dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/add-organizations.dto.ts @@ -1,5 +1,4 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; import { ArrayNotEmpty, ArrayUnique, IsArray, IsString, IsUUID } from 'class-validator'; export class AddOrganizationsDto { @@ -11,7 +10,6 @@ export class AddOrganizationsDto { @IsUUID('4', { each: true }) @ArrayUnique({ message: 'Duplicate Organization Ids are not allowed' }) @IsString({ each: true, message: 'Each organization Id in the array should be a string' }) - @Transform(({ value }) => value.map((item: string) => item.trim())) organizationIds: string[]; ecosystemId: string; diff --git a/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts b/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts index 30601d663..f2be132ff 100644 --- a/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts @@ -13,6 +13,7 @@ export class CreateEcosystemDto { @MinLength(2, { message: 'Ecosystem name must be at least 2 characters.' }) @MaxLength(50, { message: 'Ecosystem name must be at most 50 characters.' }) @IsString({ message: 'Ecosystem name must be in string format.' }) + @IsNotSQLInjection({ message: 'Ecosystem name is required.' }) name: string; @ApiProperty() diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index 51fd224f1..0724e8bb2 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -142,7 +142,7 @@ export class EcosystemController { }) async getEcosystem( @Query() paginationDto: PaginationDto, - @Param('orgId') orgId: string, + @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(`Invalid format for orgId`); }})) orgId: string, @Res() res: Response ): Promise { const ecosystemList = await this.ecosystemService.getAllEcosystem(orgId, paginationDto); diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index c59a87034..a295ee8b0 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -16,6 +16,48 @@ class Issuer { @Type(() => String) id: string | { id?: string }; } +export class Credential { + @ApiProperty() + @IsNotEmpty({ message: 'context is required' }) + @IsCredentialJsonLdContext() + '@context': Array; + + @ApiProperty() + @IsNotEmpty({ message: 'type is required' }) + type: string[]; + + @ApiProperty() + @IsString({ message: 'id should be string' }) + @IsNotEmpty({ message: 'id is required' }) + @Type(() => String) + @IsOptional() + id?:string; + + + @ApiProperty() + @ValidateNested({ each: true }) + @Type(() => Issuer) + issuer:Issuer; + + @ApiProperty() + @IsString({ message: 'issuance date should be string' }) + @IsNotEmpty({ message: 'issuance date is required' }) + @Type(() => String) + issuanceDate:string; + + @ApiProperty() + @IsString({ message: 'expiration date should be string' }) + @IsNotEmpty({ message: 'expiration date is required' }) + @Type(() => String) + @IsOptional() + expirationDate?:string; + + @ApiProperty() + @IsNotEmpty({ message: ' credential subject required' }) + credentialSubject: SingleOrArray; + [key: string]: unknown + + } class PrettyVc { @ApiPropertyOptional() @@ -102,6 +144,23 @@ export class JsonLdCredentialDetailOptions { this.proofType = options.proofType; } } +export class Attribute { + @ApiProperty() + @IsString({ message: 'Attribute name should be string' }) + @IsNotEmpty({ message: 'Attribute name is required' }) + @Transform(({ value }) => trim(value)) + @Type(() => String) + name: string; + + @ApiProperty() + @IsDefined() + @Transform(({ value }) => trim(value)) + value: string; + + @ApiProperty({ default: false }) + @IsBoolean() + @IsOptional() + isRequired?: boolean = false; @IsString() @IsNotEmpty({ message: 'proof purpose is required' }) @@ -146,77 +205,77 @@ export class Attribute { isRequired?: boolean = false; } export class CredentialsIssuanceDto { - @ApiProperty({ example: 'string' }) - @IsNotEmpty({ message: 'Credential definition Id is required' }) - @IsString({ message: 'Credential definition id should be string' }) - @Transform(({ value }) => value.trim()) - @IsOptional() - credentialDefinitionId?: string; - - @ApiProperty({ example: 'string' }) - @IsNotEmpty({ message: 'Please provide valid comment' }) - @IsString({ message: 'comment should be string' }) - @IsOptional() - comment: string; - - @ApiPropertyOptional({ example: 'v1' }) - @IsOptional() - @IsNotEmpty({ message: 'Please provide valid protocol version' }) - @IsString({ message: 'protocol version should be string' }) - protocolVersion?: string; - - @ApiPropertyOptional() - @IsOptional() - @IsNotEmpty({ message: 'Please provide valid goal code' }) - @IsString({ message: 'goal code should be string' }) - goalCode?: string; - - @ApiPropertyOptional() - @IsOptional() - @IsNotEmpty({ message: 'Please provide valid parent thread id' }) - @IsString({ message: 'parent thread id should be string' }) - parentThreadId?: string; - - @ApiPropertyOptional() - @IsOptional() - @IsNotEmpty({ message: 'Please provide valid willConfirm' }) - @IsBoolean({ message: 'willConfirm should be boolean' }) - willConfirm?: boolean; - - @ApiPropertyOptional() - @IsOptional() - @IsNotEmpty({ message: 'Please provide valid label' }) - @IsString({ message: 'label should be string' }) - label?: string; - - @ApiPropertyOptional() - @IsOptional() - @IsNotEmpty({ message: 'please provide valid imageUrl' }) - @IsString({ message: 'imageUrl must be a string' }) - imageUrl?: string; - - @ApiPropertyOptional() - @IsOptional() - @IsString({ message: 'auto accept proof must be in string' }) - @IsNotEmpty({ message: 'please provide valid auto accept proof' }) - @IsEnum(AutoAccept, { - message: `Invalid auto accept credential. It should be one of: ${Object.values(AutoAccept).join(', ')}` - }) - autoAcceptCredential?: string; - - @ApiProperty({ example: 'jsonld' }) - @IsNotEmpty({ message: 'Please provide credential type ' }) - @Transform(({ value }) => trim(value).toLocaleLowerCase()) - @IsOptional() - credentialType: IssueCredentialType; - - @ApiPropertyOptional({ default: true }) - @IsOptional() - @IsNotEmpty({ message: 'please provide valid value for reuseConnection' }) - @IsBoolean({ message: 'reuseConnection must be a boolean' }) - reuseConnection?: boolean; - - orgId: string; + @ApiProperty({ example: 'string' }) + @IsNotEmpty({ message: 'Credential definition Id is required' }) + @IsString({ message: 'Credential definition id should be string' }) + @Transform(({ value }) => value.trim()) + @IsOptional() + credentialDefinitionId?: string; + + @ApiProperty({ example: 'string' }) + @IsNotEmpty({ message: 'Please provide valid comment' }) + @IsString({ message: 'comment should be string' }) + @IsOptional() + comment: string; + + @ApiPropertyOptional({ example: 'v1' }) + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid protocol version' }) + @IsString({ message: 'protocol version should be string' }) + protocolVersion?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid goal code' }) + @IsString({ message: 'goal code should be string' }) + goalCode?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid parent thread id' }) + @IsString({ message: 'parent thread id should be string' }) + parentThreadId?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid willConfirm' }) + @IsBoolean({ message: 'willConfirm should be boolean' }) + willConfirm?: boolean; + + @ApiPropertyOptional() + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid label' }) + @IsString({ message: 'label should be string' }) + label?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsNotEmpty({ message: 'please provide valid imageUrl' }) + @IsString({ message: 'imageUrl must be a string' }) + imageUrl?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString({ message: 'auto accept proof must be in string' }) + @IsNotEmpty({ message: 'please provide valid auto accept proof' }) + @IsEnum(AutoAccept, { + message: `Invalid auto accept credential. It should be one of: ${Object.values(AutoAccept).join(', ')}` + }) + autoAcceptCredential?: string; + + @ApiProperty({ example: 'jsonld' }) + @IsNotEmpty({ message: 'Please provide credential type ' }) + @Transform(({ value }) => trim(value).toLocaleLowerCase()) + @IsOptional() + credentialType:IssueCredentialType; + + @ApiPropertyOptional({ default: true }) + @IsOptional() + @IsNotEmpty({ message: 'please provide valid value for reuseConnection' }) + @IsBoolean({ message: 'reuseConnection must be a boolean' }) + reuseConnection?: boolean; + + orgId: string; } export class OOBIssueCredentialDto extends CredentialsIssuanceDto { @@ -286,15 +345,6 @@ class CredentialOffer { @ValidateNested({ each: true }) credential?: Credential; - @ApiProperty() - @IsOptional() - @IsNotEmpty({ message: 'Please provide valid options' }) - @IsObject({ message: 'options should be an object' }) - @ValidateNested({ each: true }) - @Type(() => JsonLdCredentialDetailOptions) - options?: JsonLdCredentialDetailOptions; -} - export class IssuanceDto { @ApiProperty() @IsOptional() @@ -352,13 +402,14 @@ export class IssuanceDto { @IsOptional() contextCorrelationId: string; - @ApiPropertyOptional() - @IsOptional() - type: string; + @ApiPropertyOptional() + @IsOptional() + type: string; - @ApiProperty() - @IsOptional() - outOfBandId: string | null; + @ApiProperty() + @IsOptional() + outOfBandId: string | null; +} @ApiProperty() @IsOptional() diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 4cf67b0b2..1e1573996 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -20,9 +20,7 @@ import { Logger, BadRequestException, NotFoundException, - ParseUUIDPipe, - Delete, - ValidationPipe + ParseUUIDPipe } from '@nestjs/common'; import { ApiTags, @@ -70,7 +68,6 @@ import { RpcException } from '@nestjs/microservices'; import { user } from '@prisma/client'; import { IGetAllIssuedCredentialsDto } from './dtos/get-all-issued-credentials.dto'; import { IssueCredentialDto } from './dtos/multi-connection.dto'; -import { SchemaType } from '@credebl/enum/enum'; @Controller() @UseFilters(CustomExceptionFilter) @@ -562,61 +559,60 @@ async downloadBulkIssuanceCSVTemplate( * @param res * @returns Issuer creates a credential offer and sends it to the holder */ - @Post('/orgs/:orgId/credentials/offer') - @ApiBearerAuth() - @ApiOperation({ - summary: `Issuer create a credential offer`, - description: `Issuer creates a credential offer and sends it to the holder` - }) - @ApiQuery({ - name:'credentialType', - enum: IssueCredentialType - }) - @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER) - @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) - async sendCredential( - @User() user: IUserRequest, - @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(`Invalid format for orgId`); }})) orgId: string, - @Body() issueCredentialDto: IssueCredentialDto, - @Res() res: Response, - @Query('credentialType') credentialType: IssueCredentialType = IssueCredentialType.INDY - ): Promise { - issueCredentialDto.orgId = orgId; - issueCredentialDto.credentialType = credentialType; - - const credOffer = issueCredentialDto?.credentialData || []; - - if (IssueCredentialType.INDY !== credentialType && IssueCredentialType.JSONLD !== credentialType) { - throw new NotFoundException(ResponseMessages.issuance.error.invalidCredentialType); - } - - if (credentialType === IssueCredentialType.INDY && !issueCredentialDto.credentialDefinitionId) { - throw new BadRequestException(ResponseMessages.credentialDefinition.error.isRequired); - } - - if (issueCredentialDto.credentialType !== IssueCredentialType.INDY && !credOffer.every(offer => (!offer?.attributes || 0 === Object.keys(offer?.attributes).length))) { - throw new BadRequestException(ResponseMessages.issuance.error.attributesAreRequired); - } - - if (issueCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.credential || 0 === Object.keys(offer?.credential).length))) { - throw new BadRequestException(ResponseMessages.issuance.error.credentialNotPresent); - } - - if (issueCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.options || 0 === Object.keys(offer?.options).length))) { - throw new BadRequestException(ResponseMessages.issuance.error.optionsNotPresent); - } - const getCredentialDetails = await this.issueCredentialService.sendCredentialCreateOffer(issueCredentialDto, user); - const { statusCode, message, data} = getCredentialDetails; - - const finalResponse: IResponse = { - statusCode, - message, - data - }; - - return res.status(statusCode).json(finalResponse); - } + @Post('/orgs/:orgId/credentials/offer') + @ApiBearerAuth() + @ApiOperation({ + summary: `Issuer create a credential offer`, + description: `Issuer creates a credential offer and sends it to the holder` + }) + @ApiQuery({ + name:'credentialType', + enum: IssueCredentialType + }) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Created', type: ApiResponseDto }) + async sendCredential( + @User() user: IUserRequest, + @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(`Invalid format for orgId`); }})) orgId: string, + @Query('credentialType') credentialType: IssueCredentialType = IssueCredentialType.INDY, + @Body() issueCredentialDto: IssueCredentialDto, + @Res() res: Response + ): Promise { + issueCredentialDto.orgId = orgId; + issueCredentialDto.credentialType = credentialType; + + const credOffer = issueCredentialDto?.credentialData || []; + + if (IssueCredentialType.INDY !== credentialType && IssueCredentialType.JSONLD !== credentialType) { + throw new NotFoundException(ResponseMessages.issuance.error.invalidCredentialType); + } + + if (credentialType === IssueCredentialType.INDY && !issueCredentialDto.credentialDefinitionId) { + throw new BadRequestException(ResponseMessages.credentialDefinition.error.isRequired); + } + + if (issueCredentialDto.credentialType !== IssueCredentialType.INDY && !credOffer.every(offer => (!offer?.attributes || 0 === Object.keys(offer?.attributes).length))) { + throw new BadRequestException(ResponseMessages.issuance.error.attributesAreRequired); + } + + if (issueCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.credential || 0 === Object.keys(offer?.credential).length))) { + throw new BadRequestException(ResponseMessages.issuance.error.credentialNotPresent); + } + + if (issueCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.options || 0 === Object.keys(offer?.options).length))) { + throw new BadRequestException(ResponseMessages.issuance.error.optionsNotPresent); + } + const getCredentialDetails = await this.issueCredentialService.sendCredentialCreateOffer(issueCredentialDto, user); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.issuance.success.create, + data: getCredentialDetails + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } + /** * * @param user diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index c5f3df513..957317f99 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -3,11 +3,11 @@ import { Injectable, Inject } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; -import { ClientDetails, FileParameter, IssuanceDto, OOBCredentialDtoWithEmail, OOBIssueCredentialDto, PreviewFileDetails, TemplateDetails } from './dtos/issuance.dto'; -import { FileExportResponse, IIssuedCredentialSearchParams, IReqPayload, ITemplateFormat, IssueCredentialType, UploadedFileDetails } from './interfaces'; -import { ICredentialOfferResponse, IDeletedIssuanceRecords, IIssuedCredential } from '@credebl/common/interfaces/issuance.interface'; +import { ClientDetails, FileParameter, IssuanceDto, OOBCredentialDtoWithEmail, OOBIssueCredentialDto, PreviewFileDetails } from './dtos/issuance.dto'; +import { FileExportResponse, IIssuedCredentialSearchParams, IssueCredentialType, RequestPayload } from './interfaces'; +import { IIssuedCredential } from '@credebl/common/interfaces/issuance.interface'; import { IssueCredentialDto } from './dtos/multi-connection.dto'; -import { user } from '@prisma/client'; + @Injectable() export class IssuanceService extends BaseService { @@ -18,7 +18,7 @@ export class IssuanceService extends BaseService { super('IssuanceService'); } - sendCredentialCreateOffer(issueCredentialDto: IssueCredentialDto, user: IUserRequest): Promise { + sendCredentialCreateOffer(issueCredentialDto: IssueCredentialDto, user: IUserRequest): Promise { const payload = { comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, credentialData: issueCredentialDto.credentialData, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType, user }; diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index a999f9bff..88ae80b18 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -27,7 +27,6 @@ import { validate as isValidUUID } from 'uuid'; import { UserAccessGuard } from '../authz/guards/user-access-guard'; import { GetAllOrganizationsDto } from './dtos/get-organizations.dto'; import { PrimaryDid } from './dtos/set-primary-did.dto'; -import { TrimStringParamPipe } from '@credebl/common/cast.helper'; @UseFilters(CustomExceptionFilter) @Controller('orgs') @@ -327,7 +326,7 @@ export class OrganizationController { */ @Get('/:orgId/dids') - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.MEMBER) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER) @ApiBearerAuth() @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @@ -517,8 +516,12 @@ export class OrganizationController { @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) @ApiBearerAuth() @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @ApiParam({ + name: 'orgId' + }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard, UserAccessGuard) - async updateOrganization(@Body() updateOrgDto: UpdateOrganizationDto, @Param('orgId') orgId: string, @Res() res: Response, @User() reqUser: user): Promise { + @UsePipes(new ValidationPipe()) + async updateOrganization(@Body() updateOrgDto: UpdateOrganizationDto, @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(`Invalid format for orgId`); }})) orgId: string, @Res() res: Response, @User() reqUser: user): Promise { updateOrgDto.orgId = orgId; await this.organizationService.updateOrganization(updateOrgDto, reqUser.id, orgId); diff --git a/apps/api-gateway/src/organization/organization.service.ts b/apps/api-gateway/src/organization/organization.service.ts index 4183d5e26..d62f71803 100644 --- a/apps/api-gateway/src/organization/organization.service.ts +++ b/apps/api-gateway/src/organization/organization.service.ts @@ -6,7 +6,7 @@ import { CreateOrganizationDto } from './dtos/create-organization-dto'; import { BulkSendInvitationDto } from './dtos/send-invitation.dto'; import { UpdateUserRolesDto } from './dtos/update-user-roles.dto'; import { UpdateOrganizationDto } from './dtos/update-organization-dto'; -import { organisation, user } from '@prisma/client'; +import { organisation } from '@prisma/client'; import { IDidList, IGetOrgById, IGetOrganization } from 'apps/organization/interfaces/organization.interface'; import { IOrgUsers } from 'apps/user/interfaces/user.interface'; import { IOrgCredentials, IOrganization, IOrganizationInvitations, IOrganizationDashboard, IDeleteOrganization, IOrgActivityCount } from '@credebl/common/interfaces/organization.interface'; @@ -199,6 +199,13 @@ export class OrganizationService extends BaseService { return this.sendNatsMessage(this.serviceProxy, 'fetch-organization-dids', payload); } + async getDidList( + orgId: string + ): Promise { + const payload = { orgId }; + return this.sendNatsMessage(this.serviceProxy, 'fetch-organization-dids', payload); + } + async getOrgPofile( orgId: string ): Promise { diff --git a/apps/api-gateway/src/platform/platform.controller.ts b/apps/api-gateway/src/platform/platform.controller.ts index 980acfb02..51c757df5 100644 --- a/apps/api-gateway/src/platform/platform.controller.ts +++ b/apps/api-gateway/src/platform/platform.controller.ts @@ -12,7 +12,7 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; import { AuthGuard } from '@nestjs/passport'; import * as QRCode from 'qrcode'; -import { CredDefSortFields, SchemaType, SortFields } from '@credebl/enum/enum'; +import { CredDefSortFields, SortFields } from '@credebl/enum/enum'; import { GetAllPlatformCredDefsDto } from '../credential-definition/dto/get-all-platform-cred-defs.dto'; @Controller('') @@ -32,11 +32,6 @@ export class PlatformController { name: 'sortField', enum: SortFields, required: false - }) - @ApiQuery({ - name: 'schemaType', - enum: SchemaType, - required: false }) @ApiBearerAuth() @UseGuards(AuthGuard('jwt')) @@ -46,7 +41,7 @@ export class PlatformController { @Res() res: Response, @User() user: IUserRequestInterface ): Promise { - const { ledgerId, pageSize, searchByText, pageNumber, sorting, sortByValue, schemaType } = getAllSchemaDto; + const { ledgerId, pageSize, searchByText, pageNumber, sorting, sortByValue } = getAllSchemaDto; const schemaSearchCriteria: ISchemaSearchPayload = { ledgerId, pageNumber, diff --git a/apps/api-gateway/src/schema/dtos/get-all-schema.dto.ts b/apps/api-gateway/src/schema/dtos/get-all-schema.dto.ts index 797f924cf..1f23be64b 100644 --- a/apps/api-gateway/src/schema/dtos/get-all-schema.dto.ts +++ b/apps/api-gateway/src/schema/dtos/get-all-schema.dto.ts @@ -4,7 +4,7 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { IsEnum, IsOptional, IsUUID, Min } from 'class-validator'; import { toNumber, trim } from '@credebl/common/cast.helper'; -import { CredDefSortFields, SchemaType, SortFields, SortValue } from '@credebl/enum/enum'; +import { CredDefSortFields, SortFields, SortValue } from '@credebl/enum/enum'; export class GetAllSchemaDto { @ApiProperty({ required: false }) diff --git a/apps/api-gateway/src/schema/schema.controller.ts b/apps/api-gateway/src/schema/schema.controller.ts index 4d5bcf75e..b867105ae 100644 --- a/apps/api-gateway/src/schema/schema.controller.ts +++ b/apps/api-gateway/src/schema/schema.controller.ts @@ -20,7 +20,6 @@ import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; import { GenericSchemaDTO } from '../dtos/create-schema.dto'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; import { CredDefSortFields, SortFields } from '@credebl/enum/enum'; -import { TrimStringParamPipe } from '@credebl/common/cast.helper'; @UseFilters(CustomExceptionFilter) @Controller('orgs') @@ -44,7 +43,7 @@ export class SchemaController { async getSchemaById( @Res() res: Response, @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); }})) orgId: string, - @Param('schemaId', TrimStringParamPipe) schemaId: string + @Param('schemaId') schemaId: string ): Promise { if (!schemaId) { @@ -134,7 +133,25 @@ export class SchemaController { return res.status(HttpStatus.OK).json(finalResponse); } - + @Post('/:orgId/polygon-w3c/schemas') + @ApiOperation({ + summary: 'Create and sends a W3C-schema to the ledger.', + description: 'Create and sends a W3C-schema to the ledger.' + }) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @UseGuards(AuthGuard('jwt'), OrgRolesGuard) + @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) + async createW3CSchema(@Res() res: Response, @Body() schemaPayload: CreateW3CSchemaDto, @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); }})) orgId: string, @User() user: IUserRequestInterface): Promise { + + const schemaDetails = await this.appService.createW3CSchema(schemaPayload, orgId, user); + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.schema.success.create, + data: schemaDetails + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } + @Post('/:orgId/schemas') @ApiOperation({ summary: 'Create and register various types of schemas.', @@ -144,8 +161,11 @@ export class SchemaController { @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) - async createSchema(@Res() res: Response, @Body() schemaDetails: GenericSchemaDTO, @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); }})) orgId: string, @User() user: IUserRequestInterface): Promise { - const schemaResponse = await this.appService.createSchema(schemaDetails, user, orgId); + async createSchema(@Res() res: Response, @Body() schema: CreateSchemaDto, @Param('orgId', new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); }})) orgId: string, @User() user: IUserRequestInterface): Promise { + + schema.orgId = orgId; + const schemaDetails = await this.appService.createSchema(schema, user, schema.orgId); + const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, message: ResponseMessages.schema.success.create, diff --git a/apps/api-gateway/src/schema/schema.service.ts b/apps/api-gateway/src/schema/schema.service.ts index d6476cf29..e9d553be1 100644 --- a/apps/api-gateway/src/schema/schema.service.ts +++ b/apps/api-gateway/src/schema/schema.service.ts @@ -1,10 +1,10 @@ import { Injectable, Inject } from '@nestjs/common'; import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from '../../../../libs/service/base.service'; -import { GenericSchemaDTO } from '../dtos/create-schema.dto'; -import { ISchemaSearchPayload } from '../interfaces/ISchemaSearch.interface'; +import { CreateSchemaDto } from '../dtos/create-schema.dto'; +import { ISchemaSearchPayload, W3CSchemaPayload } from '../interfaces/ISchemaSearch.interface'; import { ISchemaInfo, IUserRequestInterface } from './interfaces'; -import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; +import { ICredDefWithPagination, ISchemaData, ISchemasWithPagination, IW3CSchema } from '@credebl/common/interfaces/schema.interface'; import { GetCredentialDefinitionBySchemaIdDto } from './dtos/get-all-schema.dto'; @Injectable() @@ -19,7 +19,11 @@ export class SchemaService extends BaseService { return this.sendNatsMessage(this.schemaServiceProxy, 'create-schema', payload); } - + createW3CSchema(schemaPayload: W3CSchemaPayload, orgId: string, user: IUserRequestInterface): Promise { + const payload = { schemaPayload, orgId, user }; + return this.sendNatsMessage(this.schemaServiceProxy, 'create-w3c-schema', payload); + } + getSchemaById(schemaId: string, orgId: string): Promise { const payload = { schemaId, orgId }; return this.sendNatsMessage(this.schemaServiceProxy, 'get-schema-by-id', payload); diff --git a/apps/api-gateway/src/verification/dto/request-proof.dto.ts b/apps/api-gateway/src/verification/dto/request-proof.dto.ts index 8188546bc..d08cfcaa4 100644 --- a/apps/api-gateway/src/verification/dto/request-proof.dto.ts +++ b/apps/api-gateway/src/verification/dto/request-proof.dto.ts @@ -1,12 +1,11 @@ import { ArrayNotEmpty, IsArray, IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumberString, IsObject, IsOptional, IsString, ValidateIf, ValidateNested, IsUUID, ArrayUnique, ArrayMaxSize } from 'class-validator'; -import { toLowerCase, trim } from '@credebl/common/cast.helper'; +import { trim } from '@credebl/common/cast.helper'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { AutoAccept } from '@credebl/enum/enum'; import { IProofFormats } from '../interfaces/verification.interface'; import { ProofRequestType } from '../enum/verification.enum'; - export class ProofRequestAttribute { @ValidateIf((obj) => obj.attributeNames === undefined) diff --git a/apps/api-gateway/src/verification/verification.controller.ts b/apps/api-gateway/src/verification/verification.controller.ts index 93a46a8fa..fd5e20a1c 100644 --- a/apps/api-gateway/src/verification/verification.controller.ts +++ b/apps/api-gateway/src/verification/verification.controller.ts @@ -12,7 +12,7 @@ import { ApiQuery, ApiExcludeEndpoint } from '@nestjs/swagger'; -import { Controller, Logger, Post, Body, Get, Query, HttpStatus, Res, UseGuards, Param, UseFilters, BadRequestException, ParseUUIDPipe, Delete } from '@nestjs/common'; +import { Controller, Logger, Post, Body, Get, Query, HttpStatus, Res, UseGuards, Param, UseFilters, BadRequestException, ParseUUIDPipe } from '@nestjs/common'; import { ApiResponseDto } from '../dtos/apiResponse.dto'; import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto'; import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto'; diff --git a/apps/connection/src/connection.controller.ts b/apps/connection/src/connection.controller.ts index 7af801ea0..f16a8557e 100644 --- a/apps/connection/src/connection.controller.ts +++ b/apps/connection/src/connection.controller.ts @@ -11,7 +11,7 @@ import { IReceiveInvitationByUrlOrg, IReceiveInvitationResponse } from './interfaces/connection.interfaces'; -import { IConnectionList, IDeletedConnectionsRecord } from '@credebl/common/interfaces/connection.interface'; +import { IConnectionList } from '@credebl/common/interfaces/connection.interface'; import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface'; import { IQuestionPayload } from './interfaces/messaging.interfaces'; import { user } from '@prisma/client'; @@ -20,17 +20,7 @@ export class ConnectionController { constructor(private readonly connectionService: ConnectionService) {} /** - * Description: Create out-of-band connection legacy invitation - * @param payload - * @returns Created connection invitation for out-of-band - */ - @MessagePattern({ cmd: 'create-connection' }) - async createLegacyConnectionInvitation(payload: IConnection): Promise { - return this.connectionService.createLegacyConnectionInvitation(payload); - } - - /** - * Description: Catch connection webhook responses and save details in connection table + * Receive connection webhook responses and save details in connection table * @param orgId * @returns Callback URL for connection and created connections details */ diff --git a/apps/connection/src/connection.repository.ts b/apps/connection/src/connection.repository.ts index 747099360..ec43c8385 100644 --- a/apps/connection/src/connection.repository.ts +++ b/apps/connection/src/connection.repository.ts @@ -50,7 +50,7 @@ export class ConnectionRepository { connectionInvitation: string, agentId: string, orgId: string, - recipientKey: string + invitationDid: string // eslint-disable-next-line camelcase ): Promise { try { @@ -60,7 +60,7 @@ export class ConnectionRepository { agentId, connectionInvitation, multiUse: true, - recipientKey + invitationDid } }); return agentDetails; diff --git a/apps/connection/src/connection.service.ts b/apps/connection/src/connection.service.ts index 9ac5eb7db..62f4469c6 100644 --- a/apps/connection/src/connection.service.ts +++ b/apps/connection/src/connection.service.ts @@ -38,83 +38,6 @@ export class ConnectionService { @Inject(CACHE_MANAGER) private cacheService: Cache ) {} - /** - * Create connection legacy invitation URL - * @param orgId - * @param user - * @returns Connection legacy invitation URL - */ - async createLegacyConnectionInvitation(payload: IConnection): Promise { - const { - orgId, - multiUseInvitation, - autoAcceptConnection, - alias, - imageUrl, - goal, - goalCode, - handshake, - handshakeProtocols, - recipientKey - } = payload; - try { - const agentDetails = await this.connectionRepository.getAgentEndPoint(orgId); - - const { agentEndPoint, id, organisation } = agentDetails; - const agentId = id; - if (!agentDetails) { - throw new NotFoundException(ResponseMessages.connection.error.agentEndPointNotFound); - } - - this.logger.log(`logoUrl:::, ${organisation.logoUrl}`); - const connectionPayload = { - multiUseInvitation: multiUseInvitation ?? true, - autoAcceptConnection: autoAcceptConnection ?? true, - alias: alias || undefined, - imageUrl: organisation.logoUrl || imageUrl || undefined, - label: organisation.name, - goal: goal || undefined, - goalCode: goalCode || undefined, - handshake: handshake || undefined, - handshakeProtocols: handshakeProtocols || undefined, - recipientKey: recipientKey || undefined - }; - - const orgAgentType = await this.connectionRepository.getOrgAgentType(agentDetails?.orgAgentTypeId); - const url = await this.getAgentUrl(orgAgentType, agentEndPoint, agentDetails?.tenantId); - const createConnectionInvitation = await this._createConnectionInvitation(connectionPayload, url, orgId); - const connectionInvitationUrl = createConnectionInvitation?.response?.invitationUrl; - const shortenedUrl = await this.storeConnectionObjectAndReturnUrl( - connectionInvitationUrl, - connectionPayload.multiUseInvitation - ); - const recipientsKey = createConnectionInvitation?.response?.recipientKey || recipientKey; - const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( - shortenedUrl, - agentId, - orgId, - recipientsKey - ); - const connectionDetailRecords: ConnectionResponseDetail = { - id: saveConnectionDetails.id, - orgId: saveConnectionDetails.orgId, - agentId: saveConnectionDetails.agentId, - connectionInvitation: saveConnectionDetails.connectionInvitation, - multiUse: saveConnectionDetails.multiUse, - createDateTime: saveConnectionDetails.createDateTime, - createdBy: saveConnectionDetails.createdBy, - lastChangedDateTime: saveConnectionDetails.lastChangedDateTime, - lastChangedBy: saveConnectionDetails.lastChangedBy, - recordId: createConnectionInvitation.response.outOfBandRecord.id, - recipientKey: saveConnectionDetails.recipientKey - }; - return connectionDetailRecords; - } catch (error) { - this.logger.error(`[createLegacyConnectionInvitation] - error in connection invitation: ${error}`); - this.handleError(error); - } - } - /** * Description: Catch connection webhook responses and save details in connection table * @param orgId @@ -680,7 +603,7 @@ export class ConnectionService { */ async createConnectionInvitation(payload: ICreateOutOfbandConnectionInvitation): Promise { try { - const { createOutOfBandConnectionInvitation } = payload; + const { alias, appendedAttachments, @@ -695,8 +618,7 @@ export class ConnectionService { orgId, routing, recipientKey, - invitationDid, - IsReuseConnection + invitationDid } = payload?.createOutOfBandConnectionInvitation; const agentDetails = await this.connectionRepository.getAgentEndPoint( @@ -708,21 +630,6 @@ export class ConnectionService { if (!agentDetails) { throw new NotFoundException(ResponseMessages.connection.error.agentEndPointNotFound); } - - let legacyinvitationDid; - if (IsReuseConnection) { - const data: agent_invitations[] = await this.connectionRepository.getInvitationDidByOrgId(orgId); - if (data && 0 < data.length) { - const [firstElement] = data; - legacyinvitationDid = firstElement?.invitationDid ?? undefined; - - this.logger.log('legacyinvitationDid:', legacyinvitationDid); - } - } - const connectionInvitationDid = invitationDid ? invitationDid : legacyinvitationDid; - - this.logger.log('connectionInvitationDid:', connectionInvitationDid); - this.logger.log(`logoUrl:::, ${organisation.logoUrl}`); const connectionPayload = { @@ -739,7 +646,7 @@ export class ConnectionService { routing: routing || undefined, messages: messages || undefined, recipientKey: recipientKey || undefined, - invitationDid: connectionInvitationDid || undefined + invitationDid: invitationDid || undefined }; const createConnectionInvitationFlag = 'connection-invitation'; @@ -756,11 +663,13 @@ export class ConnectionService { connectionInvitationUrl, connectionPayload.multiUseInvitation ); + + const invitationsDid = createConnectionInvitation?.response?.invitationDid || invitationDid; const saveConnectionDetails = await this.connectionRepository.saveAgentConnectionInvitations( shortenedUrl, agentId, orgId, - null + invitationsDid ); const connectionStorePayload: ConnectionResponseDetail = { id: saveConnectionDetails.id, @@ -773,7 +682,7 @@ export class ConnectionService { lastChangedDateTime: saveConnectionDetails.lastChangedDateTime, lastChangedBy: saveConnectionDetails.lastChangedBy, recordId: createConnectionInvitation.response.outOfBandRecord.id, - recipientKey: saveConnectionDetails.recipientKey + invitationDid: saveConnectionDetails.invitationDid }; return connectionStorePayload; } catch (error) { diff --git a/apps/connection/src/interfaces/connection.interfaces.ts b/apps/connection/src/interfaces/connection.interfaces.ts index 7a66d72d9..020e4821d 100644 --- a/apps/connection/src/interfaces/connection.interfaces.ts +++ b/apps/connection/src/interfaces/connection.interfaces.ts @@ -16,6 +16,7 @@ export interface IConnection { handshakeProtocols: string[]; orgId: string; recipientKey?: string; + invitationDid?: string } export interface IUserRequestInterface { userId: string; @@ -265,7 +266,7 @@ export interface ConnectionResponseDetail { lastChangedDateTime: Date; lastChangedBy: number; recordId: string; - recipientKey:string; + invitationDid?: string } export interface ICreateConnectionInvitation { diff --git a/apps/ecosystem/interfaces/ecosystem.interfaces.ts b/apps/ecosystem/interfaces/ecosystem.interfaces.ts index 4b23303a9..4bcced39a 100644 --- a/apps/ecosystem/interfaces/ecosystem.interfaces.ts +++ b/apps/ecosystem/interfaces/ecosystem.interfaces.ts @@ -416,72 +416,3 @@ export interface IEcosystemOrgDetails { count: Prisma.BatchPayload; ecosystemOrgs: IEcosystemOrgsData[]; } - -export interface IEcosystemEndorsementFlag { - autoEndorsement: boolean; -} - - -interface IEcosystemRole { - id: string; - name: string; - description: string; - createDateTime: Date; - lastChangedDateTime: Date; - deletedAt: Date; -} - -interface IEcosystemMemberOrgs extends IEcosystemOrgs{ - id: string; - createDateTime: Date; - lastChangedDateTime: Date; - deletedAt: Date; - ecosystemRole: IEcosystemRole; -} - -export interface IEcosystemData { - id: string; - name: string; - description: string; - tags: string; - createDateTime: Date; - createdBy: string; - lastChangedDateTime: Date; - lastChangedBy: string; - deletedAt: Date; - logoUrl: string; - autoEndorsement: boolean; - ledgers: Prisma.JsonValue; - ecosystemOrgs: IEcosystemMemberOrgs[]; -} - -interface IW3CAttributeValue { - attributeName: string; - schemaDataType: W3CSchemaDataType; - displayName: string; - isRequired: boolean; -} - -export interface ISubmitIndySchema { - schemaVersion?: string; - schemaName: string; - attributes: IAttributeValue[]; - orgId?: string; - orgDid?: string; -} -export interface ISubmitW3CSchema { - attributes: IW3CAttributeValue[]; - schemaName: string; - description: string; - schemaType: JSONSchemaType; -} -export interface ISubmitSchemaEndorsement { - type: SchemaTypeEnum; - schemaPayload: ISubmitIndySchema | ISubmitW3CSchema; -} - -export interface IschemaPayload { - schemaDetails: ISubmitSchemaEndorsement, - user: IUserRequestInterface, - orgId: string -} diff --git a/apps/ecosystem/src/ecosystem.controller.ts b/apps/ecosystem/src/ecosystem.controller.ts index f0032497b..0c1a1a819 100644 --- a/apps/ecosystem/src/ecosystem.controller.ts +++ b/apps/ecosystem/src/ecosystem.controller.ts @@ -6,10 +6,8 @@ import { AcceptRejectEcosystemInvitationDto } from '../dtos/accept-reject-ecosys import { FetchInvitationsPayload } from '../interfaces/invitations.interface'; import { EcosystemMembersPayload } from '../interfaces/ecosystemMembers.interface'; import { GetEndorsementsPayload, ISchemasResponse } from '../interfaces/endorsements.interface'; -import { IEcosystemDashboard, RequestCredDeffEndorsement, IEcosystem, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction, IEcosystemList, IEcosystemLeadOrgs, IRequestSchemaEndorsement } from '../interfaces/ecosystem.interfaces'; -import { IEcosystemDataDeletionResults, IEcosystemDetails } from '@credebl/common/interfaces/ecosystem.interface'; -import { user } from '@prisma/client'; -import { IUserRequestInterface } from 'apps/ledger/src/credential-definition/interfaces'; +import { IEcosystemDashboard, RequestCredDeffEndorsement, RequestSchemaEndorsement, IEcosystem, IEcosystemInvitation, IEcosystemInvitations, IEditEcosystem, IEndorsementTransaction, IEcosystemList, IEcosystemLeadOrgs } from '../interfaces/ecosystem.interfaces'; +import { IEcosystemDetails } from '@credebl/common/interfaces/ecosystem.interface'; // eslint-disable-next-line camelcase @Controller() diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index 15ae3e41a..69cb083d1 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -1,10 +1,10 @@ import { BadRequestException, Injectable, InternalServerErrorException, Logger } from '@nestjs/common'; import { PrismaService } from '@credebl/prisma-service'; // eslint-disable-next-line camelcase -import { Prisma, credential_definition, ecosystem, ecosystem_config, ecosystem_invitations, ecosystem_orgs, ecosystem_roles, endorsement_transaction, org_agents, org_roles, organisation, platform_config, schema, user_org_roles } from '@prisma/client'; +import { Prisma, credential_definition, ecosystem, ecosystem_config, ecosystem_invitations, ecosystem_orgs, ecosystem_roles, endorsement_transaction, org_agents, organisation, platform_config, schema } from '@prisma/client'; import { DeploymentModeType, EcosystemInvitationStatus, EcosystemOrgStatus, EcosystemRoles, endorsementTransactionStatus, endorsementTransactionType } from '../enums/ecosystem.enum'; import { updateEcosystemOrgsDto } from '../dtos/update-ecosystemOrgs.dto'; -import { CreateEcosystem, IEcosystemData, IEcosystemInvitation, IEcosystemOrgs, IEcosystemOrgsData, SaveSchema, SchemaTransactionResponse, saveCredDef } from '../interfaces/ecosystem.interfaces'; +import { CreateEcosystem, IEcosystemInvitation, IEcosystemOrgs, IEcosystemOrgsData, SaveSchema, SchemaTransactionResponse, saveCredDef } from '../interfaces/ecosystem.interfaces'; import { ResponseMessages } from '@credebl/common/response-messages'; import { NotFoundException } from '@nestjs/common'; import { CommonConstants } from '@credebl/common/common.constant'; @@ -122,24 +122,6 @@ export class EcosystemRepository { } } - async getEcosystemsCount(orgId: string): Promise { - try { - const ecosystemsCount = await this.prisma.ecosystem.count({ - where: { - ecosystemOrgs: { - some: { - orgId - } - } - } - }); - return ecosystemsCount; - } catch (error) { - this.logger.error(`[get all ecosystems by org Id] - error: ${JSON.stringify(error)}`); - throw error; - } - } - async checkOrgExists(orgId: string): Promise { try { const isOrgExists = await this.prisma.organisation.findUnique({ diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 7e23d014b..34fb91f98 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -20,7 +20,7 @@ import { EcosystemInviteTemplate } from '../templates/EcosystemInviteTemplate'; import { EmailDto } from '@credebl/common/dtos/email.dto'; import { sendEmail } from '@credebl/common/send-grid-helper-file'; import { AcceptRejectEcosystemInvitationDto } from '../dtos/accept-reject-ecosysteminvitation.dto'; -import { EcosystemConfigSettings, Invitation, LedgerLessConstant, OrgAgentType, SchemaType, SchemaTypeEnum } from '@credebl/enum/enum'; +import { EcosystemConfigSettings, Invitation, OrgAgentType, SchemaType } from '@credebl/enum/enum'; import { DeploymentModeType, EcosystemOrgStatus, @@ -48,12 +48,7 @@ import { IEditEcosystem, IEndorsementTransaction, IEcosystemList, - IEcosystemLeadOrgs, - IRequestW3CSchemaEndorsement, - ITransactionData, - IRequestIndySchemaEndorsement, - IRequestSchemaEndorsement, - IschemaPayload + IEcosystemLeadOrgs } from '../interfaces/ecosystem.interfaces'; import { GetAllSchemaList, GetEndorsementsPayload, ISchemasResponse } from '../interfaces/endorsements.interface'; import { CommonConstants } from '@credebl/common/common.constant'; @@ -445,21 +440,12 @@ export class EcosystemService { message: string; }> { try { - const [ecosystemRoleDetails, ecosystemDetails, userOrganizations, allOrgRoles] = await Promise.all([ - this.ecosystemRepository.getEcosystemRole(EcosystemRoles.ECOSYSTEM_MEMBER), - this.ecosystemRepository.getEcosystemLeadDetails(ecosystemLeadOrgs.ecosystemId), - this.ecosystemRepository.getAllOrganizationsByUserId(ecosystemLeadOrgs.userId), - this.ecosystemRepository.getAllOrgRoles() - ]); - - const ecosystemLeadOrg = ecosystemDetails?.orgId; - const ecosystemLeadOrgLedgerDetails = await this.ecosystemRepository.getAgentDetails(ecosystemLeadOrg); - const ecosystemLeadOrgLedgerId = ecosystemLeadOrgLedgerDetails?.ledgerId; + const ecosystemRoleDetails = await this.ecosystemRepository.getEcosystemRole(EcosystemRoles.ECOSYSTEM_MEMBER); - const ownerRoleId = allOrgRoles.find((role) => OrgRoles.OWNER === role.name)?.id; - const ownerOrganizations = userOrganizations - .filter((org) => org.orgRoleId === ownerRoleId) - .map((org) => org.orgId); + const getEcosystemDetails = await this.ecosystemRepository.getEcosystemLeadDetails(ecosystemLeadOrgs.ecosystemId); + const getEcosystemLeadOrg = getEcosystemDetails?.orgId; + const getEcosystemLeadOrgLedgerDetails = await this.ecosystemRepository.getAgentDetails(getEcosystemLeadOrg); + const getEcosystemLeadOrgLedgerId = getEcosystemLeadOrgLedgerDetails?.ledgerId; const { organizationIds } = ecosystemLeadOrgs; const errorOrgs: { statusCode: number; message: string; error?: string; data?: { orgId: string } }[] = []; @@ -474,66 +460,55 @@ export class EcosystemService { }; const orgAgentDetails = await this.ecosystemRepository.getAgentDetails(orgId); - const orgLedgerId = orgAgentDetails?.ledgerId; - - if (ownerOrganizations.includes(orgId)) { - if (orgAgentDetails?.orgDid) { - const existingOrg = await this.ecosystemRepository.checkOrgExistsInEcosystem( - orgId, - ecosystemLeadOrgs.ecosystemId - ); - if (orgLedgerId === ecosystemLeadOrgLedgerId) { - if (!existingOrg) { - addedOrgs.push({ - orgId, - ecosystemId: ecosystemLeadOrgs.ecosystemId, - ecosystemRoleId: ecosystemRoleDetails.id, - status: EcosystemOrgStatus.ACTIVE, - deploymentMode: DeploymentModeType.PROVIDER_HOSTED, - createdBy: ecosystemLeadOrgs.userId, - lastChangedBy: ecosystemLeadOrgs.userId - }); - successCount++; - } else { - result.statusCode = HttpStatus.CONFLICT; - result.message = ResponseMessages.ecosystem.error.orgAlreadyExists; - result.error = ResponseMessages.errorMessages.conflict; - result.data = { orgId }; - errorCount++; - } + const getOrgLedgerId = orgAgentDetails?.ledgerId; + if (orgAgentDetails?.orgDid) { + const existingOrg = await this.ecosystemRepository.checkOrgExistsInEcosystem( + orgId, + ecosystemLeadOrgs.ecosystemId + ); + if (getOrgLedgerId === getEcosystemLeadOrgLedgerId) { + if (!existingOrg) { + addedOrgs.push({ + orgId, + ecosystemId: ecosystemLeadOrgs.ecosystemId, + ecosystemRoleId: ecosystemRoleDetails.id, + status: EcosystemOrgStatus.ACTIVE, + deploymentMode: DeploymentModeType.PROVIDER_HOSTED, + createdBy: ecosystemLeadOrgs.userId, + lastChangedBy: ecosystemLeadOrgs.userId + }); + successCount++; } else { - result.statusCode = HttpStatus.BAD_REQUEST; - result.message = ResponseMessages.ecosystem.error.ledgerNotMatch; - result.error = ResponseMessages.errorMessages.badRequest; + result.statusCode = HttpStatus.CONFLICT; + result.message = `${ResponseMessages.ecosystem.error.orgAlreadyExists}`; + result.error = `${ResponseMessages.ecosystem.error.unableToAdd}`; result.data = { orgId }; errorCount++; } } else { result.statusCode = HttpStatus.BAD_REQUEST; - result.message = ResponseMessages.ecosystem.error.agentNotSpunUp; - result.error = ResponseMessages.errorMessages.badRequest; + result.message = `${ResponseMessages.ecosystem.error.ledgerNotMatch}`; + result.error = `${ResponseMessages.ecosystem.error.unableToAdd}`; result.data = { orgId }; errorCount++; } } else { - result.statusCode = HttpStatus.FORBIDDEN; - result.message = ResponseMessages.ecosystem.error.userNotHaveAccess; - result.error = ResponseMessages.errorMessages.forbidden; + result.statusCode = HttpStatus.BAD_REQUEST; + result.message = `${ResponseMessages.ecosystem.error.agentNotSpunUp}`; + result.error = `${ResponseMessages.ecosystem.error.unableToAdd}`; result.data = { orgId }; errorCount++; } - if (0 !== result.statusCode) { errorOrgs.push(result); } } - let statusCode = HttpStatus.CREATED; let message = ResponseMessages.ecosystem.success.add; let getOrgs = []; if (0 < addedOrgs?.length) { - const orgs = addedOrgs?.map((item) => item.orgId); + const orgs = addedOrgs.map((item) => item.orgId); await this.ecosystemRepository.addOrganizationInEcosystem(addedOrgs); //need to discuss @@ -544,8 +519,10 @@ export class EcosystemService { 0 < getOrgs?.length ? getOrgs?.map((item) => ({ statusCode: HttpStatus.CREATED, - message: ResponseMessages.ecosystem.success.add, - data: { orgId: item.orgId } + message: `${ResponseMessages.ecosystem.success.add}`, + data: { + orgId: item.orgId + } })) : []; const finalResult = [...errorOrgs, ...success]; @@ -1605,55 +1582,16 @@ export class EcosystemService { async submitTransaction(transactionPayload: ITransactionData): Promise<{txnPayload: object, responseMessage: string}> { try { - let txnPayload; - - const { endorsementId, orgId, ecosystemId } = transactionPayload; - - const ecosystemLeadDetails = await this.ecosystemRepository.getEcosystemLeadDetails(ecosystemId); - - const endorsementPayload = await this.ecosystemRepository.getTransactionDetailsByEndorsementId(endorsementId); - - const orgIdsToCheck = [ecosystemLeadDetails?.orgId, endorsementPayload?.['ecosystemOrgs']?.orgId]; - - if (!orgIdsToCheck.includes(orgId)) { - throw new ForbiddenException(ResponseMessages.organisation.error.orgNotMatch); - } + const { endorsementId, ecosystemId, ecosystemLeadAgentEndPoint, orgId } = transactionPayload; + const checkEndorsementRequestIsSubmitted = await this.ecosystemRepository.getEndorsementTransactionById( + endorsementId, + endorsementTransactionStatus.SUBMITED + ); - if (endorsementTransactionStatus.SUBMITED === endorsementPayload?.status) { + if (checkEndorsementRequestIsSubmitted) { throw new ConflictException(ResponseMessages.ecosystem.error.transactionSubmitted); } - const parsedRequestPayload = JSON.parse(endorsementPayload?.requestPayload); - - const responseMessage = LedgerLessConstant.NO_LEDGER === parsedRequestPayload?.schemaType - ? ResponseMessages.ecosystem.success.submitNoLedgerSchema - : ResponseMessages.ecosystem.success.submit; - - if (endorsementPayload?.type === endorsementTransactionType.W3C_SCHEMA) { - txnPayload = await this.submitW3CTransaction(transactionPayload); - } else { - txnPayload = await this.submitIndyTransaction(transactionPayload); - } - - return { txnPayload, responseMessage }; - } catch (error) { - this.logger.error(`In submit transaction: ${JSON.stringify(error)}`); - if (error?.error) { - throw new RpcException({ - statusCode: error?.error?.statusCode, - message: error?.error?.message, - error: error?.error?.error - }); - } else { - this.handleException(error); - } - } - } - - async submitIndyTransaction(transactionPayload: ITransactionData): Promise { - try { - const { endorsementId, ecosystemId, ecosystemLeadAgentEndPoint, orgId } = transactionPayload; - const endorsementTransactionPayload = await this.ecosystemRepository.getEndorsementTransactionById( endorsementId, endorsementTransactionStatus.SIGNED diff --git a/apps/issuance/src/issuance.controller.ts b/apps/issuance/src/issuance.controller.ts index 36b9ba4cd..a25713140 100644 --- a/apps/issuance/src/issuance.controller.ts +++ b/apps/issuance/src/issuance.controller.ts @@ -17,7 +17,7 @@ export class IssuanceController { } @MessagePattern({ cmd: 'send-credential-create-offer' }) - async sendCredentialCreateOffer(payload: IIssuance): Promise { + async sendCredentialCreateOffer(payload: IIssuance): Promise[]> { return this.issuanceService.sendCredentialCreateOffer(payload); } diff --git a/apps/issuance/src/issuance.repository.ts b/apps/issuance/src/issuance.repository.ts index 8f6457f3e..092637b2e 100644 --- a/apps/issuance/src/issuance.repository.ts +++ b/apps/issuance/src/issuance.repository.ts @@ -89,19 +89,18 @@ export class IssuanceRepository { } } - - async getRecipientKeyByOrgId(orgId: string): Promise { + async getInvitationDidByOrgId(orgId: string): Promise { try { return this.prisma.agent_invitations.findMany({ where: { orgId }, orderBy: { - createDateTime: 'asc' + createDateTime: 'asc' } }); } catch (error) { - this.logger.error(`Error in getRecipientKey in issuance repository: ${error.message}`); + this.logger.error(`Error in getInvitationDid in issuance repository: ${error.message}`); throw error; } } diff --git a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts index 4c6eb1d63..d536466db 100644 --- a/apps/ledger/src/schema/interfaces/schema-payload.interface.ts +++ b/apps/ledger/src/schema/interfaces/schema-payload.interface.ts @@ -90,7 +90,7 @@ export interface SchemaPayload { export interface W3CSchemaPayload { schemaPayload: SchemaPayload, orgId: string, - user: string + user: IUserRequestInterface } export interface W3CCreateSchema { diff --git a/apps/ledger/src/schema/repositories/schema.repository.ts b/apps/ledger/src/schema/repositories/schema.repository.ts index 0f6199193..edc81e8e0 100644 --- a/apps/ledger/src/schema/repositories/schema.repository.ts +++ b/apps/ledger/src/schema/repositories/schema.repository.ts @@ -5,7 +5,7 @@ import { ledgers, org_agents, org_agents_type, organisation, schema } from '@pri import { ISchema, ISchemaExist, ISchemaSearchCriteria } from '../interfaces/schema-payload.interface'; import { ResponseMessages } from '@credebl/common/response-messages'; import { AgentDetails, ISchemasWithCount } from '../interfaces/schema.interface'; -import { SchemaType, SortValue } from '@credebl/enum/enum'; +import { SortValue } from '@credebl/enum/enum'; import { ICredDefWithCount, IPlatformSchemas } from '@credebl/common/interfaces/schema.interface'; @Injectable() @@ -262,7 +262,7 @@ export class SchemaRepository { type: true }, orderBy: { - [sortField]: SortValue.DESC === sortBy ? SortValue.DESC : SortValue.ASC + [payload.sortField]: SortValue.DESC === payload.sortBy ? SortValue.DESC : SortValue.ASC }, take: Number(pageSize), skip: (pageNumber - 1) * pageSize diff --git a/apps/ledger/src/schema/schema.controller.ts b/apps/ledger/src/schema/schema.controller.ts index f2592fd18..4c8edca4b 100644 --- a/apps/ledger/src/schema/schema.controller.ts +++ b/apps/ledger/src/schema/schema.controller.ts @@ -26,10 +26,10 @@ export class SchemaController { return this.schemaService.createSchema(schemaDetails, user, orgId); } - @MessagePattern({ cmd: 'get-schemas-details' }) - async getSchemasDetails(payload: {templateIds: string[]}): Promise { - const { templateIds } = payload; - return this.schemaService.getSchemaDetails(templateIds); + @MessagePattern({ cmd: 'create-w3c-schema' }) + async createW3CSchema(payload: W3CSchemaPayload): Promise { + const {orgId, schemaPayload, user} = payload; + return this.schemaService.createW3CSchema(orgId, schemaPayload, user); } @MessagePattern({ cmd: 'get-schema-by-id' }) diff --git a/apps/ledger/src/schema/schema.service.ts b/apps/ledger/src/schema/schema.service.ts index e081984bd..36b54e5b0 100644 --- a/apps/ledger/src/schema/schema.service.ts +++ b/apps/ledger/src/schema/schema.service.ts @@ -15,16 +15,13 @@ import { ResponseMessages } from '@credebl/common/response-messages'; import { ICreateSchema, ICreateW3CSchema, IGenericSchema, IUserRequestInterface } from './interfaces/schema.interface'; import { CreateSchemaAgentRedirection, GetSchemaAgentRedirection } from './schema.interface'; import { map } from 'rxjs/operators'; -import { JSONSchemaType, LedgerLessConstant, LedgerLessMethods, OrgAgentType, SchemaType, SchemaTypeEnum } from '@credebl/enum/enum'; +import { OrgAgentType, SchemaType } from '@credebl/enum/enum'; import { ICredDefWithPagination, ISchemaData, ISchemaDetails, ISchemasWithPagination } from '@credebl/common/interfaces/schema.interface'; import { Cache } from 'cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { CommonConstants } from '@credebl/common/common.constant'; import { CommonService } from '@credebl/common'; import { W3CSchemaVersion } from './enum/schema.enum'; -import { v4 as uuidv4 } from 'uuid'; -import { networkNamespace } from '@credebl/common/common.utils'; -import { checkDidLedgerAndNetwork } from '@credebl/common/cast.helper'; @Injectable() export class SchemaService extends BaseService { @@ -184,51 +181,61 @@ export class SchemaService extends BaseService { ledgerId: getLedgerId.id, type: SchemaType.INDY }; - - if ('finished' === responseObj.schema.state) { - schemaDetails.schema.schemaName = responseObj.schema.schema.name; - schemaDetails.schema.attributes = trimmedAttributes; - schemaDetails.schema.schemaVersion = responseObj.schema.schema.version; - schemaDetails.createdBy = userId; - schemaDetails.schema.id = responseObj.schema.schemaId; - schemaDetails.changedBy = userId; - schemaDetails.orgId = orgId; - schemaDetails.issuerId = responseObj.schema.schema.issuerId; - const saveResponse = this.schemaRepository.saveSchema( - schemaDetails - ); - - const attributesArray = JSON.parse((await saveResponse).attributes); - (await saveResponse).attributes = attributesArray; - delete (await saveResponse).lastChangedBy; - delete (await saveResponse).lastChangedDateTime; - return saveResponse; - - } else if ('finished' === responseObj.state) { - schemaDetails.schema.schemaName = responseObj.schema.name; - schemaDetails.schema.attributes = trimmedAttributes; - schemaDetails.schema.schemaVersion = responseObj.schema.version; - schemaDetails.createdBy = userId; - schemaDetails.schema.id = responseObj.schemaId; - schemaDetails.changedBy = userId; - schemaDetails.orgId = orgId; - schemaDetails.issuerId = responseObj.schema.issuerId; - const saveResponse = this.schemaRepository.saveSchema( - schemaDetails - ); - - const attributesArray = JSON.parse((await saveResponse).attributes); - (await saveResponse).attributes = attributesArray; - delete (await saveResponse).lastChangedBy; - delete (await saveResponse).lastChangedDateTime; - return saveResponse; - - } else { - throw new NotFoundException( - ResponseMessages.schema.error.notCreated, - { cause: new Error(), description: ResponseMessages.errorMessages.notFound } - ); - } + schemaResponseFromAgentService = await this._createSchema(schemaPayload); + } + + const responseObj = JSON.parse(JSON.stringify(schemaResponseFromAgentService.response)); + + const indyNamespace = `${did.split(':')[2]}:${did.split(':')[3]}`; + const getLedgerId = await this.schemaRepository.getLedgerByNamespace(indyNamespace); + const schemaDetails: ISchema = { + schema: { schemaName: '', attributes: [], schemaVersion: '', id: '' }, + createdBy: `0`, + issuerId: '', + onLedgerStatus: 'Submitted on ledger', + orgId, + ledgerId: getLedgerId.id, + type: SchemaType.INDY + }; + + if ('finished' === responseObj.schema.state) { + schemaDetails.schema.schemaName = responseObj.schema.schema.name; + schemaDetails.schema.attributes = trimmedAttributes; + schemaDetails.schema.schemaVersion = responseObj.schema.schema.version; + schemaDetails.createdBy = userId; + schemaDetails.schema.id = responseObj.schema.schemaId; + schemaDetails.changedBy = userId; + schemaDetails.orgId = orgId; + schemaDetails.issuerId = responseObj.schema.schema.issuerId; + const saveResponse = this.schemaRepository.saveSchema( + schemaDetails + ); + + const attributesArray = JSON.parse((await saveResponse).attributes); + (await saveResponse).attributes = attributesArray; + delete (await saveResponse).lastChangedBy; + delete (await saveResponse).lastChangedDateTime; + return saveResponse; + + } else if ('finished' === responseObj.state) { + schemaDetails.schema.schemaName = responseObj.schema.name; + schemaDetails.schema.attributes = trimmedAttributes; + schemaDetails.schema.schemaVersion = responseObj.schema.version; + schemaDetails.createdBy = userId; + schemaDetails.schema.id = responseObj.schemaId; + schemaDetails.changedBy = userId; + schemaDetails.orgId = orgId; + schemaDetails.issuerId = responseObj.schema.issuerId; + const saveResponse = this.schemaRepository.saveSchema( + schemaDetails + ); + + const attributesArray = JSON.parse((await saveResponse).attributes); + (await saveResponse).attributes = attributesArray; + delete (await saveResponse).lastChangedBy; + delete (await saveResponse).lastChangedDateTime; + return saveResponse; + } else { throw new BadRequestException( ResponseMessages.schema.error.emptyData, @@ -255,11 +262,15 @@ export class SchemaService extends BaseService { } } - async createW3CSchema(orgId:string, schemaPayload: ICreateW3CSchema, user: string): Promise { + async createW3CSchema(orgId:string, schemaPayload: SchemaPayload, user: IUserRequestInterface): Promise { try { - let createSchema; - - const { description, attributes, schemaName} = schemaPayload; + const isSchemaExist = await this.schemaRepository.schemaExists(schemaPayload.schemaName, W3CSchemaVersion.W3C_SCHEMA_VERSION); + + if (0 !== isSchemaExist.length) { + throw new ConflictException(ResponseMessages.schema.error.exists); + } + + const { description, did, schemaAttributes, schemaName} = schemaPayload; const agentDetails = await this.schemaRepository.getAgentDetailsByOrgId(orgId); if (!agentDetails) { throw new NotFoundException(ResponseMessages.schema.error.agentDetailsNotFound, { @@ -303,37 +314,21 @@ export class SchemaService extends BaseService { orgId, schemaRequestPayload: agentSchemaPayload }; - if (schemaPayload.schemaType === JSONSchemaType.POLYGON_W3C) { - const createSchemaPayload = await this._createW3CSchema(W3cSchemaPayload); - createSchema = createSchemaPayload.response; - createSchema.type = JSONSchemaType.POLYGON_W3C; - } else { - const createSchemaPayload = await this._createW3CledgerAgnostic(schemaObject); - if (!createSchemaPayload) { - throw new BadRequestException(ResponseMessages.schema.error.schemaUploading, { - cause: new Error(), - description: ResponseMessages.errorMessages.badRequest - }); - } - createSchema = createSchemaPayload.data; - createSchema.did = agentDetails.orgDid; - createSchema.type = JSONSchemaType.LEDGER_LESS; - createSchema.schemaUrl = `${process.env.SCHEMA_FILE_SERVER_URL}${createSchemaPayload.data.schemaId}`; - } - - const storeW3CSchema = await this.storeW3CSchemas(createSchema, user, orgId, attributes); + const createSchema = await this._createW3CSchema(W3cSchemaPayload); + + const storeW3CSchema = await this.storeW3CSchemas(createSchema.response, user, orgId); if (!storeW3CSchema) { - throw new BadRequestException(ResponseMessages.schema.error.storeW3CSchema, { + throw new ConflictException(ResponseMessages.schema.error.storeW3CSchema, { cause: new Error(), description: ResponseMessages.errorMessages.notFound }); } - return storeW3CSchema; + return createSchema.response; } catch (error) { this.logger.error(`[createSchema] - outer Error: ${JSON.stringify(error)}`); - throw error; + throw new RpcException(error.response ? error.response : error); } } @@ -520,45 +515,50 @@ export class SchemaService extends BaseService { return W3CSchema; } - private async storeW3CSchemas(schemaDetails, user, orgId, attributes): Promise { - let ledgerDetails; + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + private async storeW3CSchemas(schemaDetails, user, orgId) { + const schemaServerUrl = `${process.env.SCHEMA_FILE_SERVER_URL}${schemaDetails.schemaId}`; + const schemaRequest = await this.commonService .httpGet(schemaServerUrl) .then(async (response) => response); + if (!schemaRequest) { throw new NotFoundException(ResponseMessages.schema.error.W3CSchemaNotFOund, { cause: new Error(), description: ResponseMessages.errorMessages.notFound }); } - const indyNamespace = await networkNamespace(schemaDetails?.did); - if (indyNamespace === LedgerLessMethods.WEB || indyNamespace === LedgerLessMethods.KEY) { - ledgerDetails = await this.schemaRepository.getLedgerByNamespace(LedgerLessConstant.NO_LEDGER); - } else { - ledgerDetails = await this.schemaRepository.getLedgerByNamespace(indyNamespace); + const schemaAttributeJson = schemaRequest.definitions.credentialSubject.properties; + const extractedData = []; + + for (const key in schemaAttributeJson) { + if (2 < Object.keys(schemaAttributeJson[key]).length) { + const { type, title } = schemaAttributeJson[key]; + const schemaDataType = type; + const displayName = title; + const isRequired = false; + extractedData.push({ 'attributeName': title, schemaDataType, displayName, isRequired }); + } } + const indyNamespace = schemaDetails?.did.includes(':testnet:') ? 'polygon:testnet' : 'polygon'; + const getLedgerId = await this.schemaRepository.getLedgerByNamespace(indyNamespace); - if (!ledgerDetails) { - throw new NotFoundException(ResponseMessages.schema.error.networkNotFound, { - cause: new Error(), - description: ResponseMessages.errorMessages.notFound - }); - } const storeSchemaDetails = { schema: { schemaName: schemaRequest.title, schemaVersion: W3CSchemaVersion.W3C_SCHEMA_VERSION, - attributes, + attributes:extractedData, id: schemaDetails.schemaUrl }, issuerId: schemaDetails.did, - createdBy: user, - changedBy: user, + createdBy: user.id, + changedBy: user.id, publisherDid: schemaDetails.did, orgId, - ledgerId: ledgerDetails.id, + ledgerId: getLedgerId.id, type: SchemaType.W3C_Schema }; const saveResponse = await this.schemaRepository.saveSchema( @@ -609,12 +609,7 @@ export class SchemaService extends BaseService { ).toPromise() .catch(error => { this.logger.error(`Error in creating W3C schema : ${JSON.stringify(error)}`); - throw new HttpException( - { - status: error.error.code, - error: error.message, - message: error.error.message.error.message - }, error.error); + throw new Error(error.error ? error.error.message : error.message); }); return W3CSchemaResponse; } diff --git a/apps/organization/interfaces/organization.interface.ts b/apps/organization/interfaces/organization.interface.ts index 6c8ccf50e..f3a2cce56 100644 --- a/apps/organization/interfaces/organization.interface.ts +++ b/apps/organization/interfaces/organization.interface.ts @@ -173,74 +173,6 @@ export interface IDidDetails { } export interface IPrimaryDidDetails extends IPrimaryDid { - id: string - networkId: string + id: string, didDocument: Prisma.JsonValue } - -export interface OrgInvitation { - id: string; - createDateTime: Date; - createdBy: string; - lastChangedDateTime: Date; - lastChangedBy: string; - deletedAt: Date; - userId: string; - orgId: string; - status: string; - orgRoles: string[]; - email: string; -} - -export interface ILedgerNameSpace { - id: string; - createDateTime: Date; - lastChangedDateTime: Date; - name: string; - networkType: string; - poolConfig: string; - isActive: boolean; - networkString: string; - nymTxnEndpoint: string; - indyNamespace: string; - networkUrl: string; -} - -export interface IGetDids { - id: string; - createDateTime: Date; - createdBy: string; - lastChangedDateTime: Date; - lastChangedBy: string; - orgId: string; - isPrimaryDid: boolean; - did: string; - didDocument: Prisma.JsonValue; - orgAgentId: string; -} - -export interface ILedgerDetails { - id: string; - createDateTime: Date; - lastChangedDateTime: Date; - name: string; - networkType: string; - poolConfig: string; - isActive: boolean; - networkString: string; - nymTxnEndpoint: string; - indyNamespace: string; - networkUrl: string; - -} - -export interface IOrgRoleDetails { - id: string; - name: string; - description: string; - createDateTime: Date; - createdBy: string; - lastChangedDateTime: Date; - lastChangedBy: string; - deletedAt: Date; -} diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index 775dd1a3f..3b13946e3 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -6,7 +6,7 @@ import { ConflictException, Injectable, Logger, NotFoundException } from '@nestj import { Prisma, agent_invitations, org_agents, org_invitations, user, user_org_roles } from '@prisma/client'; import { CreateOrganizationDto } from '../dtos/create-organization.dto'; -import { IGetDids, IDidDetails, IDidList, IGetOrgById, IGetOrganization, IPrimaryDidDetails, IUpdateOrganization, ILedgerNameSpace, OrgInvitation, ILedgerDetails, IOrgRoleDetails } from '../interfaces/organization.interface'; +import { IDidDetails, IDidList, IGetOrgById, IGetOrganization, IPrimaryDidDetails, IUpdateOrganization } from '../interfaces/organization.interface'; import { InternalServerErrorException } from '@nestjs/common'; import { Invitation, PrismaTables, SortValue } from '@credebl/enum/enum'; import { PrismaService } from '@credebl/prisma-service'; @@ -621,9 +621,6 @@ export class OrganizationRepository { logoUrl: true, orgSlug: true, createDateTime: true, - countryId:true, - stateId: true, - cityId: true, ecosystemOrgs: { select: { ecosystemId: true @@ -877,7 +874,7 @@ export class OrganizationRepository { createDateTime: true, did: true, lastChangedDateTime: true, - isPrimaryDid: true + isPrimaryDid: true } }); } catch (error) { @@ -888,164 +885,75 @@ export class OrganizationRepository { async setOrgsPrimaryDid(primaryDidDetails: IPrimaryDidDetails): Promise { try { - const {did, didDocument, id, orgId, networkId} = primaryDidDetails; + const {did, didDocument, id, orgId} = primaryDidDetails; await this.prisma.$transaction([ this.prisma.org_dids.update({ where: { id }, - data: { - isPrimaryDid: true - } - }), - this.prisma.org_agents.update({ - where: { - orgId - }, - data: { - orgDid: did, - didDocument, - ledgerId: networkId - } - }) - ]); - return ResponseMessages.organisation.success.didDetails; + data: { + isPrimaryDid: true + } + }), + this.prisma.org_agents.update({ + where: { + orgId + }, + data: { + orgDid: did, + didDocument + } + }) + ]); + return ResponseMessages.organisation.success.didDetails; } catch (error) { - this.logger.error(`[setOrgsPrimaryDid] - Update DID details: ${JSON.stringify(error)}`); - throw error; + this.logger.error(`[setOrgsPrimaryDid] - Update DID details: ${JSON.stringify(error)}`); + throw error; } - } +} async getDidDetailsByDid(did:string): Promise { - try { - return this.prisma.org_dids.findFirstOrThrow({ - where: { - did - } - }); - } catch (error) { + try { + return this.prisma.org_dids.findFirstOrThrow({ + where: { + did + } + }); + } catch (error) { this.logger.error(`[getDidDetailsByDid] - get DID details: ${JSON.stringify(error)}`); throw error; - } } + } async getPerviousPrimaryDid(orgId:string): Promise { - try { - return this.prisma.org_dids.findFirstOrThrow({ - where: { - orgId, - isPrimaryDid: true - } - }); - } catch (error) { + try { + return this.prisma.org_dids.findFirstOrThrow({ + where: { + orgId, + isPrimaryDid: true + } + }); + } catch (error) { this.logger.error(`[getPerviousPrimaryDid] - get DID details: ${JSON.stringify(error)}`); throw error; - } - } - - async getDids(orgId:string): Promise { - try { - return this.prisma.org_dids.findMany({ - where: { - orgId - } - }); - } catch (error) { - this.logger.error(`[getDids] - get all DIDs: ${JSON.stringify(error)}`); - throw error; - } } + } async setPreviousDidFlase(id:string): Promise { - try { - return this.prisma.org_dids.update({ - where: { - id - }, - data: { - isPrimaryDid: false - } - }); - } catch (error) { + try { + return this.prisma.org_dids.update({ + where: { + id + }, + data: { + isPrimaryDid: false + } + }); + } catch (error) { this.logger.error(`[setPreviousDidFlase] - Update DID details: ${JSON.stringify(error)}`); throw error; - } - } - - async getOrgInvitationsByOrg(orgId: string): Promise { - try { - return this.prisma.org_invitations.findMany({ - where: { - orgId - } - }); - } catch (error) { - this.logger.error(`[getOrgInvitationsByOrg] - get organization invitations: ${JSON.stringify(error)}`); - throw error; - } - } - - async getNetworkByNameSpace(nameSpace: string): Promise { - try { - return this.prisma.ledgers.findFirstOrThrow({ - where: { - indyNamespace: nameSpace - } - }); - } catch (error) { - this.logger.error(`[getNetworkByIndyNameSpace] - get network by namespace: ${JSON.stringify(error)}`); - throw error; - } - } - - async getLedger(name: string): Promise { - try { - const ledgerData = await this.prisma.ledgers.findFirstOrThrow({ - where: { - name - } - }); - return ledgerData; - } catch (error) { - this.logger.error(`[getLedger] - get ledger details: ${JSON.stringify(error)}`); - throw error; - } - } - - async getOrgRole(id: string[]): Promise { - try { - const orgRoleData = await this.prisma.org_roles.findMany({ - where: { - id: { - in: id - } - } - }); - return orgRoleData; - } catch (error) { - this.logger.error(`[getOrgRole] - get org role details: ${JSON.stringify(error)}`); - throw error; - } } + } - async getUserOrgRole(userId: string, orgId: string): Promise { - try { - const userOrgRoleDetails = await this.prisma.user_org_roles.findMany({ - where: { - userId, - orgId - }, - select:{ - orgRoleId: true - } - }); - // Map the result to an array of orgRoleId - const orgRoleIds = userOrgRoleDetails.map(role => role.orgRoleId); - return orgRoleIds; - } catch (error) { - this.logger.error(`[getUserOrgRole] - get user org role details: ${JSON.stringify(error)}`); - throw error; - } - } } diff --git a/apps/organization/src/organization.controller.ts b/apps/organization/src/organization.controller.ts index ea5daada5..f37c918ea 100644 --- a/apps/organization/src/organization.controller.ts +++ b/apps/organization/src/organization.controller.ts @@ -5,8 +5,8 @@ import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { UpdateInvitationDto } from '../dtos/update-invitation.dt'; import { IDidList, IGetOrgById, IGetOrganization, IUpdateOrganization, Payload } from '../interfaces/organization.interface'; -import { IOrgCredentials, IOrganizationInvitations, IOrganization, IOrganizationDashboard, IDeleteOrganization, IOrgActivityCount } from '@credebl/common/interfaces/organization.interface'; -import { organisation, user } from '@prisma/client'; +import { organisation } from '@prisma/client'; +import { IOrgCredentials, IOrganizationInvitations, IOrganization, IOrganizationDashboard } from '@credebl/common/interfaces/organization.interface'; import { IAccessTokenData } from '@credebl/common/interfaces/interface'; import { IClientRoles } from '@credebl/client-registration/interfaces/client.interface'; @@ -80,20 +80,6 @@ export class OrganizationController { const { userId, pageNumber, pageSize, search, role } = payload; return this.organizationService.getOrganizations(userId, pageNumber, pageSize, search, role); } - /** - * Description: get organization count - * @param - * @returns Get created organization details - */ - @MessagePattern({ cmd: 'get-organizations-count' }) - async countTotalOrgs( - @Body() payload: { userId: string} - ): Promise { - - const { userId } = payload; - - return this.organizationService.countTotalOrgs(userId); - } /** * @returns Get public organization details diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index 49d041fbf..ed197eb2d 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -25,7 +25,7 @@ import { sendEmail } from '@credebl/common/send-grid-helper-file'; import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { UpdateInvitationDto } from '../dtos/update-invitation.dt'; -import { DidMethod, Invitation, Ledgers, PrismaTables, transition } from '@credebl/enum/enum'; +import { Invitation, OrgAgentType, transition } from '@credebl/enum/enum'; import { IGetOrgById, IGetOrganization, IUpdateOrganization, IOrgAgent, IClientCredentials, ICreateConnectionUrl, IOrgRole, IDidList, IPrimaryDidDetails } from '../interfaces/organization.interface'; import { UserActivityService } from '@credebl/user-activity'; import { ClientRegistrationService } from '@credebl/client-registration/client-registration.service'; @@ -172,6 +172,69 @@ export class OrganizationService { * @returns */ + // eslint-disable-next-line camelcase + async setPrimaryDid( + orgId:string, + did:string, + id:string + ): Promise { + try { + const organizationExist = await this.organizationRepository.getOrgProfile(orgId); + if (!organizationExist) { + throw new NotFoundException(ResponseMessages.organisation.error.notFound); + } + const orgAgentDetails = await this.organizationRepository.getAgentEndPoint(orgId); + if (orgAgentDetails.orgDid === did) { + throw new ConflictException(ResponseMessages.organisation.error.primaryDid); + } + //check user DID exist in the organization's did list + const organizationDidList = await this.organizationRepository.getAllOrganizationDid(orgId); + const isDidMatch = organizationDidList.some(item => item.did === did); + + if (!isDidMatch) { + throw new NotFoundException(ResponseMessages.organisation.error.didNotFound); + } + const didDetails = await this.organizationRepository.getDidDetailsByDid(did); + + if (!didDetails) { + throw new NotFoundException(ResponseMessages.organisation.error.didNotFound); + } + const primaryDidDetails: IPrimaryDidDetails = { + did, + orgId, + id, + didDocument: didDetails.didDocument + }; + + const setPrimaryDid = await this.organizationRepository.setOrgsPrimaryDid(primaryDidDetails); + + const getExistingPrimaryDid = await this.organizationRepository.getPerviousPrimaryDid(orgId); + + + if (!getExistingPrimaryDid) { + throw new NotFoundException(ResponseMessages.organisation.error.didNotFound); + } + + const setPriviousDidFalse = await this.organizationRepository.setPreviousDidFlase(getExistingPrimaryDid.id); + + + await Promise.all([setPrimaryDid, getExistingPrimaryDid, setPriviousDidFalse]); + + + return ResponseMessages.organisation.success.primaryDid; + + } catch (error) { + this.logger.error(`In setPrimaryDid method: ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } + + /** + * + * @param registerOrgDto + * @returns + */ + // eslint-disable-next-line camelcase async setPrimaryDid( orgId:string, @@ -571,7 +634,13 @@ export class OrganizationService { * @returns Get created organizations details */ - async getOrganizations(userId: string, pageNumber: number, pageSize: number, search: string): Promise { + async getOrganizations( + userId: string, + pageNumber: number, + pageSize: number, + search: string, + role?: string + ): Promise { try { const query = { @@ -588,12 +657,7 @@ export class OrganizationService { userId }; - const getOrgs = await this.organizationRepository.getOrganizations( - query, - filterOptions, - pageNumber, - pageSize - ); + const getOrgs = await this.organizationRepository.getOrganizations(query, filterOptions, pageNumber, pageSize, role, userId); return getOrgs; } catch (error) { @@ -1844,4 +1908,18 @@ export class OrganizationService { throw new RpcException(error.response ? error.response : error); } } + + /** + * + * @param orgId + * @returns fetch organization did list + */ + async getOrgDidList(orgId: string): Promise { + try { + return await this.organizationRepository.getAllOrganizationDid(orgId); + } catch (error) { + this.logger.error(`get Org dids: ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } } diff --git a/apps/user/interfaces/user.interface.ts b/apps/user/interfaces/user.interface.ts index 4db0a736d..9fabb6f4b 100644 --- a/apps/user/interfaces/user.interface.ts +++ b/apps/user/interfaces/user.interface.ts @@ -120,31 +120,31 @@ export interface IUserCredentials { export interface IOrgUsers { totalPages: number, users: OrgUser[] -} - -export interface IDidList { - id: string; - createDateTime: Date; - did: string; - lastChangedDateTime: Date; - isPrimaryDid: boolean; -} - -interface OrgUser { - id: string; - username: string; - email: string; - firstName: string; - lastName: string; - isEmailVerified: boolean; - userOrgRoles: UserOrgRoles[]; -} - -interface UserOrgRoles { - id: string; - orgId: string; - orgRoleId: string; - orgRole: OrgRole; + } + + export interface IDidList { + id: string; + createDateTime: Date; + did: string; + lastChangedDateTime: Date; + isPrimaryDid: boolean; + } + + interface OrgUser { + id: string; + username: string; + email: string; + firstName: string; + lastName: string; + isEmailVerified: boolean; + userOrgRoles: UserOrgRoles[]; + } + + interface UserOrgRoles { + id: string; + orgId: string; + orgRoleId: string; + orgRole: OrgRole; organisation: Organization } interface OrgRole { diff --git a/apps/verification/src/interfaces/verification.interface.ts b/apps/verification/src/interfaces/verification.interface.ts index ecbab48dd..ced4b84c1 100644 --- a/apps/verification/src/interfaces/verification.interface.ts +++ b/apps/verification/src/interfaces/verification.interface.ts @@ -160,9 +160,11 @@ export interface ISendProofRequestPayload { emailId?: string[] isShortenUrl?: boolean; type?:string; + orgId?: string; presentationDefinition?:IProofRequestPresentationDefinition; reuseConnection?: boolean; recipientKey?:string; + invitationDid?: string } export interface IWSendProofRequestPayload { @@ -184,7 +186,7 @@ export interface IWSendProofRequestPayload { export interface IProofRequestPayload { url: string; apiKey?: string; - orgId?: string + orgId?: string; proofRequestPayload: ISendProofRequestPayload | ISendPresentationExchangeProofRequestPayload; } diff --git a/apps/verification/src/repositories/verification.repository.ts b/apps/verification/src/repositories/verification.repository.ts index da64ebb21..abb36692b 100644 --- a/apps/verification/src/repositories/verification.repository.ts +++ b/apps/verification/src/repositories/verification.repository.ts @@ -130,20 +130,6 @@ export class VerificationRepository { const { proofPresentationPayload, orgId } = payload; - //For Educreds - if (proofPresentationPayload?.['proofData']?.presentation?.presentationExchange?.verifiableCredential) { - - const emailId = proofPresentationPayload?.['proofData']?.presentation?.presentationExchange?.verifiableCredential[0].credentialSubject?.email; - encryptEmailId = await this.commonService.dataEncryption(emailId); - } else { - encryptEmailId = 'Not Available'; - } - - //For Educreds - if (proofPresentationPayload?.['proofData']?.request?.presentationExchange) { - schemaId = proofPresentationPayload?.['proofData']?.request?.presentationExchange?.presentation_definition?.input_descriptors[0].schema[0].uri; - } - if ('default' !== proofPresentationPayload?.contextCorrelationId) { const getOrganizationId = await this.getOrganizationByTenantId(proofPresentationPayload?.contextCorrelationId); organisationId = getOrganizationId?.orgId; @@ -224,9 +210,9 @@ export class VerificationRepository { throw error; } } - + // eslint-disable-next-line camelcase - async getRecipientKeyByOrgId(orgId: string): Promise { + async getInvitationDidByOrgId(orgId: string): Promise { try { return this.prisma.agent_invitations.findMany({ where: { @@ -237,7 +223,7 @@ export class VerificationRepository { } }); } catch (error) { - this.logger.error(`Error in getRecipientKey in verification repository: ${error.message}`); + this.logger.error(`Error in getInvitationDid in verification repository: ${error.message}`); throw error; } } diff --git a/apps/verification/src/verification.service.ts b/apps/verification/src/verification.service.ts index 63d554eff..2fec92269 100644 --- a/apps/verification/src/verification.service.ts +++ b/apps/verification/src/verification.service.ts @@ -4,9 +4,9 @@ import { ClientProxy, RpcException } from '@nestjs/microservices'; import { map } from 'rxjs/operators'; import { IGetAllProofPresentations, IProofRequestSearchCriteria, IGetProofPresentationById, IProofPresentation, IProofRequestPayload, IRequestProof, ISendProofRequestPayload, IVerifyPresentation, IVerifiedProofData, IInvitation } from './interfaces/verification.interface'; import { VerificationRepository } from './repositories/verification.repository'; -import { ATTRIBUTE_NAME_REGEX, CommonConstants } from '@credebl/common/common.constant'; -import { RecordType, agent_invitations, org_agents, organisation, presentations, user } from '@prisma/client'; -import { AutoAccept, OrgAgentType, VerificationProcessState } from '@credebl/enum/enum'; +import { CommonConstants } from '@credebl/common/common.constant'; +import { agent_invitations, org_agents, organisation, presentations } from '@prisma/client'; +import { AutoAccept, OrgAgentType } from '@credebl/enum/enum'; import { ResponseMessages } from '@credebl/common/response-messages'; import * as QRCode from 'qrcode'; import { OutOfBandVerification } from '../templates/out-of-band-verification.template'; @@ -401,15 +401,15 @@ export class VerificationService { // Destructuring 'outOfBandRequestProof' to remove emailId, as it is not used while agent operation const { isShortenUrl, emailId, type, reuseConnection, ...updateOutOfBandRequestProof } = outOfBandRequestProof; - let recipientKey: string | undefined; + let invitationDid: string | undefined; if (true === reuseConnection) { - const data: agent_invitations[] = await this.verificationRepository.getRecipientKeyByOrgId(user.orgId); + const data: agent_invitations[] = await this.verificationRepository.getInvitationDidByOrgId(user.orgId); if (data && 0 < data.length) { const [firstElement] = data; - recipientKey = firstElement?.recipientKey ?? undefined; + invitationDid = firstElement?.invitationDid ?? undefined; } } - outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || 'always'; + outOfBandRequestProof.autoAcceptProof = outOfBandRequestProof.autoAcceptProof || AutoAccept.Always; let payload: IProofRequestPayload; @@ -417,7 +417,6 @@ export class VerificationService { if (ProofRequestType.INDY === type) { updateOutOfBandRequestProof.protocolVersion = updateOutOfBandRequestProof.protocolVersion || 'v1'; updateOutOfBandRequestProof.invitationDid = invitationDid || undefined; - updateOutOfBandRequestProof.imageUrl = getOrganization?.logoUrl || undefined; payload = { orgId: user.orgId, url, @@ -446,7 +445,7 @@ export class VerificationService { } }, autoAcceptProof:outOfBandRequestProof.autoAcceptProof, - recipientKey:recipientKey || undefined + invitationDid:invitationDid || undefined } }; } @@ -741,6 +740,7 @@ export class VerificationService { } } + // TODO: This function is only for anoncreds indy async getVerifiedProofdetails(proofId: string, orgId: string): Promise { try { const getAgentDetails = await this.verificationRepository.getAgentEndPoint(orgId); diff --git a/libs/common/src/interfaces/agent-service.interface.ts b/libs/common/src/interfaces/agent-service.interface.ts index 235e00210..26613351d 100644 --- a/libs/common/src/interfaces/agent-service.interface.ts +++ b/libs/common/src/interfaces/agent-service.interface.ts @@ -11,6 +11,7 @@ export interface InvitationMessage { }; outOfBandRecord: OutOfBandRecord; recipientKey?:string + invitationDid?: string }; } diff --git a/libs/common/src/interfaces/connection.interface.ts b/libs/common/src/interfaces/connection.interface.ts index 5dd59f804..646b46429 100644 --- a/libs/common/src/interfaces/connection.interface.ts +++ b/libs/common/src/interfaces/connection.interface.ts @@ -31,6 +31,7 @@ export interface IConnectionsListCount { lastChangedDateTime: Date; lastChangedBy: number; recipientKey?:string; + invitationDid?: string } export interface IDeletedConnectionsRecord { diff --git a/libs/common/src/interfaces/ecosystem.interface.ts b/libs/common/src/interfaces/ecosystem.interface.ts index 3e35440e3..ec0591688 100644 --- a/libs/common/src/interfaces/ecosystem.interface.ts +++ b/libs/common/src/interfaces/ecosystem.interface.ts @@ -8,7 +8,7 @@ interface EcosystemRole { lastChangedDateTime: Date; deletedAt: Date; } - + interface Ecosystem { id: string; name: string; diff --git a/libs/common/src/interfaces/schema.interface.ts b/libs/common/src/interfaces/schema.interface.ts index 68372ae15..238974ad5 100644 --- a/libs/common/src/interfaces/schema.interface.ts +++ b/libs/common/src/interfaces/schema.interface.ts @@ -40,29 +40,6 @@ export interface ISchemasWithPagination extends IPaginationDetails{ issuerId: string; } - interface Attribute { - attributeName: string; - schemaDataType: string; - displayName: string; - isRequired: boolean; - } - - export interface ISchemaDetail { - id: string; - createDateTime: string; - createdBy: string; - lastChangedDateTime: string; - lastChangedBy: string; - name: string; - version: string; - attributes: Attribute[]; - schemaLedgerId: string; - publisherDid: string; - issuerId: string; - orgId: string; - ledgerId: string; - type: string; - } export interface IPlatformSchemas { schemasCount: number; @@ -89,26 +66,3 @@ export interface ISchemasWithPagination extends IPaginationDetails{ export interface INetworkUrl { networkUrl: string; } - - export interface ISchemaFields { - name?: string; - schemaName?: string; - attributes?: IIndySchemaAttributesValue[]; - schemaAttributes?: IW3CSchemaAttributesValue[]; - endorse?: boolean; - version?: string; - did?: string; - description?: string; - } - - interface IIndySchemaAttributesValue { - attributeName: string; - schemaDataType: string; - displayName: string; - } - - interface IW3CSchemaAttributesValue { - title: string; - type: string; - } - \ No newline at end of file diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index a2b044dcd..19af77b00 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -28,70 +28,60 @@ export const ResponseMessages = { stateVerificationCode: 'All states has been fetched successfully', cityVerificationCode: 'All cities has been fetched successfully' }, - error: { - exists: 'User already exists', - profileNotFound: 'User public profile not found', - notUpdatePlatformSettings: 'Unable to update platform config settings', - platformSetttingsNotFound: 'Unable to get platform settings', - ecosystemSetttingsNotFound: 'Unable to get ecosystem settings', - notUpdateEcosystemSettings: 'Unable to update ecosystem config settings', - verificationAlreadySent: 'The verification link has already been sent to your email address', - emailSend: 'Unable to send email to the user', - redirectUrlNotFound: 'Redirect URL not found', - invalidEmailUrl: 'Invalid verification code or EmailId!', - verifiedEmail: 'Email already verified', - notFound: 'User not found', - verifyMail: 'Please verify your email', - invalidCredentials: 'Invalid Credentials', - registerFido: 'Please complete your fido registration', - invitationNotFound: 'Invitation not found for this user', - invitationAlreadyAccepted: 'Organization invitation already accepted', - invitationAlreadyRejected: 'Organization invitation already rejected', - invalidInvitationStatus: 'Invalid invitation status', - invalidKeycloakId: 'keycloakId is invalid', - invalidEmail: 'Invalid Email Id!', - adduser: 'Unable to add user details', - userRoleNotFound: 'User role not found', - verifyEmail: 'The verification link has already been sent to your email address. please verify', - emailNotVerified: 'The verification link has already been sent to your email address. please verify', - userNotRegisterd: 'The user has not yet completed the registration process', - InvalidEmailDomain: 'Email from this domain is not allowed', - credentialNotFound: 'User credentials not found', - invalidOrgId: 'Organization does not exist', - invalidInvitationId: 'Organization invitation does not exist', - invitationAlreadyPending: 'Organization invitation is already in pending state', - emailIsNotVerified: 'Email is not verified', - invitationStatusUpdateInvalid: 'Status update is invalid. Request is already', - resetSamePassword: 'New password should not be the current password', - resetPasswordLink: 'Unable to create reset password token', - invalidResetLink: 'Invalid or expired reset password link', - invalidAccessToken: 'Authentication failed', - invalidRefreshToken: 'Invalid refreshToken provided', - userOrgsLimit: 'Limit reached: You can be associated with or create maximum 10 organizations.' - } - }, - organisation: { - success: { - create: 'Organization created successfully', - update: 'Organization updated successfully', - fetchProfile: 'Organization profile fetched successfully', - fetchOrgRoles: 'Organization roles fetched successfully', - createInvitation: 'Organization invitations sent', - getInvitation: 'Organization invitations fetched successfully', - getOrganization: 'Organization details fetched successfully', - getOrgDashboard: 'Organization dashboard details fetched', - getOrganizations: 'Organizations details fetched successfully', - updateUserRoles: 'User roles updated successfully', - delete: 'Organization deleted successfully', - orgInvitationDeleted: 'Organization invitation deleted successfully', - orgCredentials: 'Organization credentials created successfully', - fetchedOrgCredentials: 'Organization credentials fetched successfully', - clientCredentials: 'Client credentials fetched successfully', - deleteCredentials: 'Organization client credentials deleted', - orgDids: 'Organization DIDs fetched successfully', - primaryDid: 'Primary DID updated successfully', - didDetails: 'DID Details updated sucessfully', - getOrganizationActivity: 'Organization activity count fetched successfully' + organisation: { + success: { + create: 'Organization created successfully', + update: 'Organization updated successfully', + fetchProfile: 'Organization profile fetched successfully', + fetchOrgRoles: 'Organization roles fetched successfully', + createInvitation: 'Organization invitations sent', + getInvitation: 'Organization invitations fetched successfully', + getOrganization: 'Organization details fetched successfully', + getOrgDashboard: 'Organization dashboard details fetched', + getOrganizations: 'Organizations details fetched successfully', + updateUserRoles: 'User roles updated successfully', + delete: 'Organization deleted successfully', + orgInvitationDeleted: 'Organization invitation deleted successfully', + orgCredentials: 'Organization credentials created successfully', + fetchedOrgCredentials: 'Organization credentials fetched successfully', + clientCredentials: 'Client credentials fetched successfully', + deleteCredentials:'Organization client credentials deleted', + orgDids: 'Organization DIDs fetched successfully', + primaryDid: 'Primary DID updated successfully', + didDetails: 'DID Details updated sucessfully' + + }, + error: { + exists: 'An organization name is already exist', + orgProfileNotFound: 'Organization public profile not found', + orgSlugIsRequired: 'orgSlug is required', + invitationIdIsRequired:'Invitation Id is required', + userIdIsRequired:'UserId is required', + rolesNotExist: 'Provided roles not exists in the platform', + orgProfile: 'Organization profile not found', + userNotFound: 'User not found for the given organization', + orgRoleIdNotFound:'Provided roles not exists for this organization', + updateUserRoles: 'Unable to update user roles', + deleteOrg: 'Organization not found', + deleteOrgInvitation: 'Organization does not have access to delete this invitation', + notFound: 'Organization agent not found', + orgNotFound: 'Organization not found', + orgNotMatch: 'Organization does not have access', + invitationStatusInvalid: 'Unable to delete invitation with accepted/rejected status', + credentialsNotUpdate: 'Unable to update organization credentials', + invalidOrgId:'Invalid format for orgId', + orgIdIsRequired:'OrgId is required', + clientIdRequired:'clientId is required', + notExistClientCred: 'Organization does not have client credentials', + invalidUserId:'Invalid format of userId', + invalidInvitationId:'Invalid format for invitation id', + ecosystemIdIsRequired:'ecosystemId is required', + roleNotMatch: 'User does not have access', + orgDoesNotMatch: 'Organization does not match', + invalidClient: 'Invalid client credentials', + primaryDid: 'This DID is already set to primary DID', + didNotFound: 'DID does not exist in organiation' + } }, error: { exists: 'An organization name is already exist', @@ -152,131 +142,108 @@ export const ResponseMessages = { } }, - schema: { - success: { - fetch: 'Schema retrieved successfully.', - create: 'Schema created successfully.' + schema: { + success: { + fetch: 'Schema retrieved successfully.', + create: 'Schema created successfully.' + }, + error: { + invalidSchemaId: 'Invalid schema Id provided.', + invalidData: 'Invalid data provided.', + nameNotEmpty: 'Schema name is required', + versionNotEmpty: 'Schema version is required', + invalidVersion: 'Invalid schema version provided.', + insufficientAttributes: 'Please provide at least one attribute.', + uniqueAttributesnames: 'Please provide unique attribute names', + uniqueAttributesDisplaynames: 'Please provide unique display names for attributes', + emptyData: 'Please provide data for creating schema.', + exists: 'Schema already exists', + notCreated: 'Schema not created', + notFound: 'Schema records not found', + schemaIdNotFound: 'SchemaLedgerId not found', + credentialDefinitionNotFound: 'No credential definition exist', + notStoredCredential: 'User credential not stored', + agentDetailsNotFound: 'Agent details not found', + failedFetchSchema: 'Failed to fetch schema data', + atLeastOneRequired: 'At least one of the attributes should have isReuired as `true`', + schemaBuilder: 'Error while creating schema JSON', + W3CSchemaNotFOund: 'Error while resolving W3C schema', + storeW3CSchema: 'Error while storing W3C schema' + } }, - error: { - invalidSchemaId: 'Invalid schema Id provided.', - invalidData: 'Invalid data provided.', - nameNotEmpty: 'Schema name is required', - versionNotEmpty: 'Schema version is required', - invalidVersion: 'Invalid schema version provided.', - insufficientAttributes: 'Please provide at least one attribute.', - uniqueAttributesnames: 'Please provide unique attribute names', - uniqueAttributesDisplaynames: 'Please provide unique display names for attributes', - emptyData: 'Please provide data for creating schema.', - exists: 'Schema already exists', - notCreated: 'Schema not created', - notFound: 'Schema records not found', - schemaIdNotFound: 'SchemaLedgerId not found', - credentialDefinitionNotFound: 'No credential definition exist', - notStoredCredential: 'User credential not stored', - agentDetailsNotFound: 'Agent details not found', - failedFetchSchema: 'Failed to fetch schema data', - atLeastOneRequired: 'At least one of the attributes should have isReuired as `true`', - schemaBuilder: 'Error while creating schema JSON', - schemaUploading: 'Error while uploading schema JSON', - W3CSchemaNotFOund: 'Error while resolving W3C schema', - storeW3CSchema: 'Error while storing W3C schema', - networkNotFound: 'Error while fetching network', - orgDidAndSchemaType: 'Organization DID and schema type does not match' - } - }, - credentialDefinition: { - success: { - fetch: 'Credential definition fetched successfully.', - create: 'Credential definition created successfully.', - template: 'Credential template fetched successfully.' + credentialDefinition: { + success: { + fetch: 'Credential definition fetched successfully.', + create: 'Credential definition created successfully.' + }, + error: { + NotFound: 'No credential definitions found.', + NotSaved: 'Error in saving credential definition.', + Conflict: 'Credential definition already exists', + schemaIdNotFound: 'SchemaLedgerId not found', + isRequired: 'Credential definition Id is required', + OrgDidNotFound: 'OrgDid not found', + credDefIdNotFound: 'Credential Definition Id not found' + } }, - error: { - NotFound: 'No credential definitions found.', - NotSaved: 'Error in saving credential definition.', - Conflict: 'Credential definition already exists', - schemaIdNotFound: 'SchemaLedgerId not found', - isRequired: 'Credential definition Id is required', - OrgDidNotFound: 'OrgDid not found', - credDefIdNotFound: 'Credential Definition Id not found', - InvalidSchemaType: 'Invalid schema type or not supported yet' - } - }, - ledger: { - success: { - fetch: 'Ledger details retrieved successfully.', - fetchNetworkUrl: 'Network url retrieved successfully' + ledger: { + success: { + fetch: 'Ledger details retrieved successfully.', + fetchNetworkUrl: 'Network url retrieved successfully' + }, + error: { + NotFound: 'No ledgers found.' + } }, - error: { - NotFound: 'No ledgers found.' - } - }, - agent: { - success: { - create: 'Agent process initiated successfully. Please wait', - createWallet: 'Wallet created successfully', - createDid: 'Did created successfully', - health: 'Agent health details retrieved successfully.', - ledgerConfig: 'Ledger config details fetched successfully.', - webhookUrlRegister: 'Webhook Url registered successfully', - getWebhookUrl: 'Webhook Url fetched successfully', - createKeys: 'Key-pair created successfully', - walletDelete: 'The wallet has been deleted.' - }, - error: { - exists: 'An agent name is already exist', - orgNotFound: 'Organization not found', - apiEndpointNotFound: 'apiEndpoint not found', - notAbleToSpinUpAgent: 'Agent not able to spin up', - alreadySpinUp: 'Agent already spun up', - agentUrl: 'Agent url not exist', - apiKeyNotExist: 'API key is not found', - walletNotDeleted: 'Wallet is not deleted, Please check', - seedChar: 'seed must be at most 32 characters', - validWalletName: 'Please enter valid wallet name. It allows only alphanumeric values', - platformConfiguration: 'Platform configuration is missing or invalid', - apiEndpoint: 'API endpoint is missing in the platform configuration', - externalIp: 'External IP is missing in the platform configuration', - stringExternalIp: 'External IP must be a string', - agentProcess: 'Agent process is invalid or not in a completed state', - notAbleToSpinup: 'Agent not able to spun up', - ledgerNotFound: 'Ledgers not found', - 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 spun up', - platformAdminNotAbleToSpinp: 'Platform admin agent is not spun up', - invalidLedger: 'Invalid ledger name', - seedCharCount: 'seed must be at most 32 characters', - nullTenantId: 'TenantId must not be null', - tenantIdNotFound: 'TenantId not found', - invalidTenantIdIdFormat: 'Invalid tenantId format', - requiredTenantId: 'Tenant Id is required', - createDid: 'Error while creating DID', - networkMismatch: 'The network is mismatched.', - didAlreadyExist: 'DID already exist', - storeDid: 'Error while storing DID', - noLedgerFound: 'No ledger data not found.', - agentSpinupError: 'Agent endpoint unreachable', - agentEndpointRequired: 'Agent endpoint is required', - failedAgentType: 'Agent endpoint is required', - failedApiKey: 'Failed to encrypt API key', - failedOrganization: 'Failed to fetch organization agent type details', - promiseReject: 'One or more promises were rejected.', - orgAgentNotFound: 'Org agent type not found', - walletDoesNotExists: 'Organization wallet does not exists' - } - }, - connection: { - success: { - create: 'Connection created successfully', - receivenvitation: 'Invitation received successfully', - fetchConnection: 'Connection details fetched successfully', - fetch: 'Connections details fetched successfully', - questionAnswerRecord: 'Question Answer record fetched successfully', - questionSend: 'Question sent successfully', - deleteConnectionRecord: 'Connection records deleted', - basicMessage: 'Basic message sent successfully' + agent: { + success: { + create: 'Agent process initiated successfully. Please wait', + createWallet: 'Wallet created successfully', + createDid: 'Did created successfully', + health: 'Agent health details retrieved successfully.', + ledgerConfig: 'Ledger config details fetched successfully.', + webhookUrlRegister:'Webhook Url registered successfully', + getWebhookUrl:'Webhook Url fetched successfully', + createKeys:'Key-pair created successfully' + }, + error: { + exists: 'An agent name is already exist', + orgNotFound: 'Organization not found', + apiEndpointNotFound: 'apiEndpoint not found', + notAbleToSpinUpAgent: 'Agent not able to spin up', + alreadySpinUp: 'Agent already spun up', + agentUrl: 'Agent url not exist', + apiKeyNotExist:'API key is not found', + seedChar: 'seed must be at most 32 characters', + validWalletName: 'Please enter valid wallet name. It allows only alphanumeric values', + platformConfiguration: 'Platform configuration is missing or invalid', + apiEndpoint: 'API endpoint is missing in the platform configuration', + externalIp: 'External IP is missing in the platform configuration', + stringExternalIp: 'External IP must be a string', + agentProcess: 'Agent process is invalid or not in a completed state', + notAbleToSpinup: 'Agent not able to spun up', + ledgerNotFound: 'Ledgers not found', + 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 spun up', + platformAdminNotAbleToSpinp: 'Platform admin agent is not spun up', + invalidLedger: 'Invalid ledger name', + seedCharCount: 'seed must be at most 32 characters', + nullTenantId:'TenantId must not be null', + tenantIdNotFound:'TenantId not found', + invalidTenantIdIdFormat:'Invalid tenantId format', + requiredTenantId:'Tenant Id is required', + createDid:'Error while creating DID', + storeDid: 'Error while storing DID', + agentSpinupError: 'Agent endpoint unreachable', + agentEndpointRequired: 'Agent endpoint is required', + failedAgentType: 'Agent endpoint is required', + failedApiKey: 'Failed to encrypt API key', + failedOrganization: 'Failed to fetch organization agent type details', + promiseReject: 'One or more promises were rejected.' + } }, error: { exists: 'Connection is already exist', @@ -300,50 +267,45 @@ export const ResponseMessages = { bulkProcess: 'Process initiated for bulk issuance', deleteIssuanceRecords: 'Issuance records deleted' }, - error: { - exists: 'Credentials is already exist', - credentialsNotFound: 'Credentials not found', - agentEndPointNotFound: 'Agent details not found', - organizationNotFound: 'organization Not Found', - agentUrlNotFound: 'agent url not found', - notFound: 'History not found', - credentialOfferNotFound: 'Credential offer not found', - invitationNotFound: 'Invitation not found', - unableToCreateOOBOffer: 'Unable to create out-of-band credential offer', - platformConfigNotFound: 'Platform config details not found', - emailSend: 'Unable to send email to the user', - previewFile: 'Error while fetching file details', - previewCachedData: 'Error while fetching cached data', - emptyFileData: 'File details does not exit or removed', - cacheTimeOut: 'Timeout for reviewing data, re-upload your file and generate new request', - fileNotFound: 'File details not found', - fileData: 'File data does not exist for the specific file', - retry: 'Credentials do not exist for retry', - walletError: 'Credential Issuance failed due to error in Wallet Agent', - emailIdNotPresent: 'EmailId is empty or not present', - attributesNotPresent: 'Attributes are not present or not empty', - unableToCreateOffer: 'Unable to create offer', - orgAgentTypeNotFound: 'Organization agent type not found', - credentialNotPresent: 'credential is required', - optionsNotPresent: 'options are required', - attributesAreRequired: 'attributes are required', - invalidCredentialType: 'invalid credential type', - missingRequestId: 'Param requestId is missing from the request.', - cachedData: 'Cached data does not exist', - cachedfileData: 'Cached file data does not exist', - storeBulkData: 'Error while storing the bulk deata', - issuanceRecordsNotFound: 'Issuance records does not exists', - removeIssuanceData: 'First you have to remove issuance data' - } - }, - verification: { - success: { - fetch: 'Proof presentations details fetched successfully.', - create: 'Presentation of proof received successfully.', - verifiedProofDetails: 'Proof presentation details fetched successfully.', - send: 'Proof request send successfully.', - verified: 'Proof presentation verified successfully.', - deleteVerificationRecord: 'Verification records deleted' + issuance: { + success: { + create: 'Credentials offer created successfully', + createOOB: 'Out-of-band credentials offer created successfully', + fetch: 'Issued Credential details fetched successfully', + importCSV: 'File imported successfully', + previewCSV: 'File details fetched successfully', + bulkIssuance: 'Issuance process started. It will take some time', + notFound: 'Schema records not found' + }, + error: { + exists: 'Credentials is already exist', + credentialsNotFound: 'Credentials not found', + agentEndPointNotFound: 'Agent details not found', + organizationNotFound: 'organization Not Found', + agentUrlNotFound: 'agent url not found', + notFound: 'History not found', + credentialOfferNotFound: 'Credential offer not found', + invitationNotFound: 'Invitation not found', + unableToCreateOOBOffer: 'Unable to create out-of-band credential offer', + platformConfigNotFound: 'Platform config details not found', + emailSend: 'Unable to send email to the user', + previewFile: 'Error while fetching file details', + previewCachedData: 'Error while fetching cached data', + emptyFileData: 'File details does not exit or removed', + cacheTimeOut: 'Timeout for reviewing data, re-upload your file and generate new request', + fileNotFound: 'File details not found', + fileData: 'File data does not exist for the specific file', + retry: 'Credentials do not exist for retry', + walletError: 'Credential Issuance failed due to error in Wallet Agent', + emailIdNotPresent: 'EmailId is empty or not present', + attributesNotPresent: 'Attributes are not present or not empty', + unableToCreateOffer: 'Unable to create offer', + orgAgentTypeNotFound: 'Organization agent type not found', + credentialNotPresent: 'credential is required', + optionsNotPresent:'options are required', + attributesAreRequired: 'attributes are required', + invalidCredentialType:'invalid credential type' + } }, error: { notFound: 'Organization agent not found', @@ -387,69 +349,78 @@ export const ResponseMessages = { allschema: 'Schema details fetched successfully', AutoSignAndSubmit: 'Endorsement request approved & submitted to ledger' }, - error: { - notCreated: 'Error while creating ecosystem', - agentNotSpunUp: 'Agent is not spun up for this organization', - userNotHaveAccess: 'You do not have access', - orgAlreadyExists: 'Organization is already exists in ecosystem', - unableToAdd: 'Unable to add organization', - partiallyAdded: 'Organization(s) are partially added', - orgNotExist: 'Organization does not exist', - orgDidNotExist: 'Organization did does not exist', - exists: 'An ecosystem name is already exist', - update: 'Error while updating ecosystem', - invalidInvitationStatus: 'Invalid invitation status', - invitationNotFound: 'Ecosystem Invitation not found', - invitationNotUpdate: 'Ecosystem Invitation not updated', - ledgerNotMatch: 'Organization ledger network not matched with Ecosystem', - orgsNotUpdate: 'Ecosystem Orgs not updated', - ecosystemNotEnabled: 'Ecosystem service is not enabled', - sumbitTransaction: 'Error while submitting transaction', - signTransactionNotApplicable: 'Signing transaction for w3c schema is not aapllicable', - requestSchemaTransaction: 'Error while request schema transaction', - requestCredDefTransaction: 'Error while submitting transaction', - notFound: 'Organization not found', - platformConfigNotFound: 'Platform configurations not found', - schemaNotFound: 'Schema not found', - ecosystemNotFound: 'Ecosystem not found', - ecosystemOrgNotFound: 'Ecosystem org not found', - ecosystemConfigNotFound: 'Ecosystem config not found', - credentialDefinitionNotFound: 'Credential definition found', - leadNotFound: 'Lead details not found', - signRequestError: 'Error while signing the transaction', - updateTransactionError: 'Error while update the transaction', - schemaAlreadyExist: 'Schema name and schema version already exist', - schemaNameAlreadyExist: 'Schema name already exist', - credDefAlreadyExist: 'Credential definition already exist', - saveSchema: 'Error while storing the schema details', - saveCredDef: 'Error while storing the credential-definition details', - invalidOrgId: 'Invalid organization Id', - invalidEcosystemId: 'Invalid ecosystem Id', - invalidTransaction: 'Transaction does not exist', - transactionSubmitted: 'Transaction already submitted', - transactionAlreadySigned: 'Transaction already signed', - transactionNotSigned: 'Transaction request is not signed', - transactionNotRequested: 'Transaction is not requested', - invalidAgentUrl: 'Invalid agent url', - EndorsementTransactionNotFoundException: 'Endorsement transaction with status requested not found', - OrgOrEcosystemNotFoundExceptionForEndorsementTransaction: 'The endorsement transaction status cant be updated', - ecosystemOrgAlready: - 'Organization is already part of the ecosystem. Please ensure that the organization is not duplicated.', - updateSchemaId: 'Error while updating the schema id', - updateCredDefId: 'Error while updating the credential-definition', - invalidMessage: 'Invalid transaction details. Missing "message" property.', - invalidTransactionMessage: 'Invalid transaction details', - ecosystemRoleNotMatch: 'Ecosystem role not match', - orgEcoIdRequired: 'OrgId & EcosystemId is required', - ecosystemMembersNotExists: 'Ecosystem members does not exists', - notAbleToDeleteEcosystem: 'You cannot delete the ecosystem, because you are the ecosystem lead', - ecosystemNotExists: 'Ecosystem does not exists' - } - }, - bulkIssuance: { - success: { - create: 'Issuance process initiated successfully', - reinitiated: 'Process reinitiated for bulk issuance' + ecosystem: { + success: { + create: 'Ecosystem created successfully', + update: 'Ecosystem details updated successfully', + add: 'Organization added successfully', + delete: 'Ecosystem invitations deleted successfully', + fetch: 'Ecosystem fetched successfully', + getEcosystemDashboard: 'Ecosystem dashboard details fetched successfully', + getInvitation: 'Ecosystem invitations fetched successfully', + createInvitation: 'Ecosystem invitations sent', + schemaRequest: 'Schema transaction request created successfully', + credDefRequest: 'Credential definition transaction request created successfully', + sign: 'Endorsement request approved', + submit: 'Endorsement request submitted to ledger', + invitationReject: 'Ecosystem invitation rejected', + invitationAccept: 'Ecosystem invitation accepted successfully', + fetchEndorsors: 'Endorser transactions fetched successfully', + DeclineEndorsementTransaction: 'Endorsement request declined', + AutoEndorsementTransaction: 'The flag for transactions has been successfully set', + fetchMembers: 'Ecosystem members fetched successfully', + allschema: 'Schema details fetched successfully', + AutoSignAndSubmit: 'Endorsement request approved & submitted to ledger' + }, + error: { + notCreated: 'Error while creating ecosystem', + agentNotSpunUp: 'Agent is not spun up for this organization', + orgAlreadyExists: 'Organization is already exists in ecosystem', + unableToAdd: 'Unable to add organization', + partiallyAdded: 'Organization(s) are partially added', + orgNotExist: 'Organization does not exist', + orgDidNotExist: 'Organization did does not exist', + exists: 'An ecosystem name is already exist', + update: 'Error while updating ecosystem', + invalidInvitationStatus: 'Invalid invitation status', + invitationNotFound: 'Ecosystem Invitation not found', + invitationNotUpdate: 'Ecosystem Invitation not updated', + ledgerNotMatch: 'Organization ledger network not matched with Ecosystem', + orgsNotUpdate: 'Ecosystem Orgs not updated', + ecosystemNotEnabled: 'Ecosystem service is not enabled', + sumbitTransaction: 'Error while submitting transaction', + requestSchemaTransaction: 'Error while request schema transaction', + requestCredDefTransaction: 'Error while submitting transaction', + notFound: 'Organization not found', + platformConfigNotFound: 'Platform configurations not found', + schemaNotFound: 'Schema not found', + ecosystemNotFound: 'Ecosystem not found', + ecosystemOrgNotFound: 'Ecosystem org not found', + ecosystemConfigNotFound: 'Ecosystem config not found', + credentialDefinitionNotFound: 'Credential definition found', + leadNotFound: 'Lead details not found', + signRequestError: 'Error while signing the transaction', + updateTransactionError: 'Error while update the transaction', + schemaAlreadyExist: 'Schema name and schema version already exist', + credDefAlreadyExist: 'Credential definition already exist', + saveSchema: 'Error while storing the schema details', + saveCredDef: 'Error while storing the credential-definition details', + invalidOrgId: 'Invalid organization Id', + invalidEcosystemId: 'Invalid ecosystem Id', + invalidTransaction: 'Transaction does not exist', + transactionSubmitted: 'Transaction already submitted', + transactionNotSigned: 'Transaction request is not signed', + invalidAgentUrl: 'Invalid agent url', + EndorsementTransactionNotFoundException: 'Endorsement transaction with status requested not found', + OrgOrEcosystemNotFoundExceptionForEndorsementTransaction: 'The endorsement transaction status cant be updated', + ecosystemOrgAlready: 'Organization is already part of the ecosystem. Please ensure that the organization is not duplicated.', + updateSchemaId: 'Error while updating the schema id', + updateCredDefId: 'Error while updating the credential-definition', + invalidMessage: 'Invalid transaction details. Missing "message" property.', + invalidTransactionMessage: 'Invalid transaction details', + ecosystemRoleNotMatch: 'Ecosystem role not match', + orgEcoIdRequired: 'OrgId & EcosystemId is required' + } }, error: { PathNotFound: 'Path to export data not found.', diff --git a/libs/prisma-service/prisma/data/credebl-master-table.json b/libs/prisma-service/prisma/data/credebl-master-table.json index ea8714d16..9e0fd86b0 100644 --- a/libs/prisma-service/prisma/data/credebl-master-table.json +++ b/libs/prisma-service/prisma/data/credebl-master-table.json @@ -132,8 +132,7 @@ "poolConfig": "", "isActive": true, "networkString": "testnet", - "registerDIDEndpoint": "", - "registerDIDPayload": "", + "nymTxnEndpoint": "", "indyNamespace": "polygon:testnet" }, { diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index d32f305e1..8eec4a21c 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -318,7 +318,8 @@ model agent_invitations { lastChangedBy Int @default(1) org_agents org_agents @relation(fields: [agentId], references: [id]) organisation organisation @relation(fields: [orgId], references: [id]) - recipientKey String? + recipientKey String? + invitationDid String? } model connections { @@ -517,6 +518,7 @@ model notification { lastChangedBy String @default("1") deletedAt DateTime? @db.Timestamp(6) } + model ledgerConfig { id String @id @default(uuid()) @db.Uuid name String @@ -527,104 +529,3 @@ model ledgerConfig { lastChangedBy String @default("1") deletedAt DateTime? @db.Timestamp(6) } - -model user_org_delete_activity { - id String @id @default(uuid()) @db.Uuid - userEmail String @default("") - userId String @db.Uuid - orgId String @db.Uuid - recordType RecordType - txnMetadata Json - deletedBy String @db.Uuid - deleteDateTime DateTime @default(now()) @db.Timestamptz(6) -} - -enum RecordType { - VERIFICATION_RECORD - ISSUANCE_RECORD - CONNECTION - ECOSYSTEM_MEMBER - ORGANIZATION - WALLET - INVITATION -} - -model countries { - id Int @id @default(autoincrement()) @map("id") - name String @map("name") - states states[] - cities cities[] - organisation organisation[] - - @@map("countries") -} - -model states { - id Int @id @default(autoincrement()) @map("id") - name String @map("name") - countryId Int @map("country_id") - countryCode String @map("country_code") - countries countries @relation(fields: [countryId], references: [id], onDelete: Cascade) - cities cities[] - organisation organisation[] - - @@map("states") -} - -model cities { - id Int @id @default(autoincrement()) @map("id") - name String @map("name") - stateId Int @map("state_id") - stateCode String @map("state_code") - countryId Int @map("country_id") - countryCode String @map("country_code") - state states @relation(fields: [stateId], references: [id], onDelete: Cascade) - country countries @relation(fields: [countryId], references: [id], onDelete: Cascade) - organisation organisation[] - - @@index([stateId], name: "cities_stateId_idx") - @@index([countryId], name: "cities_countryId_idx") - @@map("cities") -} - -model user_role { - id String @id @default(uuid()) @db.Uuid - role UserRole - user_role_mapping user_role_mapping[] -} - -enum UserRole { - DEFAULT_USER - HOLDER -} - -model user_role_mapping { - id String @id @default(uuid()) @db.Uuid - userId String @db.Uuid - user user @relation(fields: [userId], references: [id]) - userRoleId String @db.Uuid - user_role user_role @relation(fields: [userRoleId], references: [id]) -} - -model cloud_wallet_user_info { - id String @id @default(uuid()) @db.Uuid - label String? - tenantId String? - email String? @unique() @db.VarChar(500) - type CloudWalletType - createDateTime DateTime @default(now()) @db.Timestamptz(6) - createdBy String @db.Uuid - lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) - lastChangedBy String @db.Uuid - userId String? @db.Uuid - agentEndpoint String? - agentApiKey String? - key String? - connectionImageUrl String? - user user? @relation(fields: [userId], references: [id]) -} - -enum CloudWalletType { - CLOUD_BASE_WALLET - CLOUD_SUB_WALLET -}