diff --git a/apps/agent-service/src/repositories/agent-service.repository.ts b/apps/agent-service/src/repositories/agent-service.repository.ts index 852099aff..81c82d1ea 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 { Injectable, Logger } from '@nestjs/common'; +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 } from '@credebl/enum/enum'; +import { AgentType, PrismaTables } from '@credebl/enum/enum'; @Injectable() export class AgentServiceRepository { @@ -490,8 +490,26 @@ export class AgentServiceRepository { // eslint-disable-next-line camelcase deleteOrgAgent: org_agents; }> { + const tablesToCheck = [ + `${PrismaTables.CONNECTIONS}`, + `${PrismaTables.CREDENTIALS}`, + `${PrismaTables.PRESENTATIONS}`, + `${PrismaTables.ECOSYSTEM_INVITATIONS}`, + `${PrismaTables.ECOSYSTEM_ORGS}` + ]; + try { return await this.prisma.$transaction(async (prisma) => { + const referenceCounts = await Promise.all( + tablesToCheck.map(table => prisma[table].count({ where: { orgId } })) + ); + + referenceCounts.forEach((count, index) => { + if (0 < count) { + throw new ConflictException(`Organization ID ${orgId} is referenced in the table ${tablesToCheck[index]}`); + } + }); + // Concurrently delete related records const [orgDid, agentInvitation] = await Promise.all([ prisma.org_dids.deleteMany({ where: { orgId } }), 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 7f7149a99..d65d01da1 100644 --- a/apps/api-gateway/src/agent-service/agent-service.controller.ts +++ b/apps/api-gateway/src/agent-service/agent-service.controller.ts @@ -13,7 +13,8 @@ import { Get, UseFilters, Param, - Delete + Delete, + ParseUUIDPipe } from '@nestjs/common'; import { ApiTags, @@ -43,7 +44,7 @@ import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; import { validateDid } from '@credebl/common/did.validator'; import { CreateWalletDto } from './dto/create-wallet.dto'; import { CreateNewDidDto } from './dto/create-new-did.dto'; -import { AgentSpinupValidator } from '@credebl/common/cast.helper'; +import { AgentSpinupValidator, TrimStringParamPipe } from '@credebl/common/cast.helper'; import { AgentConfigureDto } from './dto/agent-configure.dto'; const seedLength = 32; @@ -307,7 +308,7 @@ export class AgentController { @Roles(OrgRoles.OWNER) @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) async deleteWallet( - @Param('orgId') orgId: string, + @Param('orgId', TrimStringParamPipe, new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); }})) orgId: string, @User() user: user, @Res() res: Response ): Promise { diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 98f009328..610361f34 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -27,6 +27,7 @@ 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') @@ -544,7 +545,7 @@ export class OrganizationController { @UseGuards(AuthGuard('jwt')) @Roles(OrgRoles.OWNER) async deleteOrganization( - @Param('orgId') orgId: string, + @Param('orgId', TrimStringParamPipe, new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); }})) orgId: string, @User() user: user, @Res() res: Response ): Promise { diff --git a/apps/ledger/src/schema/repositories/schema.repository.ts b/apps/ledger/src/schema/repositories/schema.repository.ts index dbe42aee2..b582acc8a 100644 --- a/apps/ledger/src/schema/repositories/schema.repository.ts +++ b/apps/ledger/src/schema/repositories/schema.repository.ts @@ -217,7 +217,7 @@ export class SchemaRepository { schemaLedgerId: true, createdBy: true, publisherDid: true, - orgId: true, + orgId: true, // This field can be null issuerId: true }, orderBy: { @@ -232,7 +232,14 @@ export class SchemaRepository { ledgerId: payload.ledgerId } }); - return { schemasCount, schemasResult }; + + // Handle null orgId in the response + const schemasWithDefaultOrgId = schemasResult.map(schema => ({ + ...schema, + orgId: schema.orgId || null // Replace null orgId with 'N/A' or any default value + })); + + return { schemasCount, schemasResult: schemasWithDefaultOrgId }; } catch (error) { this.logger.error(`Error in getting schemas: ${error}`); throw error; diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index ee2f050cd..826c562ad 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -8,7 +8,7 @@ import { Prisma, agent_invitations, org_agents, org_invitations, user, user_org_ import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { IGetDids, IDidDetails, IDidList, IGetOrgById, IGetOrganization, IPrimaryDidDetails, IUpdateOrganization, ILedgerNameSpace, OrgInvitation, ILedgerDetails } from '../interfaces/organization.interface'; import { InternalServerErrorException } from '@nestjs/common'; -import { Invitation, SortValue } from '@credebl/enum/enum'; +import { Invitation, PrismaTables, SortValue } from '@credebl/enum/enum'; import { PrismaService } from '@credebl/prisma-service'; import { UserOrgRolesService } from '@credebl/user-org-roles'; import { organisation } from '@prisma/client'; @@ -767,16 +767,16 @@ export class OrganizationRepository { deleteOrg: IDeleteOrganization }> { const tablesToCheck = [ - 'org_agents', - 'org_dids', - 'agent_invitations', - 'connections', - 'credentials', - 'presentations', - 'ecosystem_invitations', - 'ecosystem_orgs', - 'file_upload', - 'notification' + `${PrismaTables.ORG_AGENTS}`, + `${PrismaTables.ORG_DIDS}`, + `${PrismaTables.AGENT_INVITATIONS}`, + `${PrismaTables.CONNECTIONS}`, + `${PrismaTables.CREDENTIALS}`, + `${PrismaTables.PRESENTATIONS}`, + `${PrismaTables.ECOSYSTEM_INVITATIONS}`, + `${PrismaTables.ECOSYSTEM_ORGS}`, + `${PrismaTables.FILE_UPLOAD}`, + `${PrismaTables.NOTIFICATION}` ]; try { @@ -812,6 +812,16 @@ export class OrganizationRepository { const deletedOrgInvitations = await prisma.org_invitations.deleteMany({ where: { orgId: id } }); + await this.prisma.schema.updateMany({ + where: { orgId: id }, + data: { orgId: null } + }); + + await this.prisma.credential_definition.updateMany({ + where: { orgId: id }, + data: { orgId: null } + }); + // If no references are found, delete the organization const deleteOrg = await prisma.organisation.delete({ where: { id } }); diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index ed652175d..b671b84e7 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -388,7 +388,7 @@ export class UserService { try { try { - const data = jwt.decode(refreshToken); + const data = jwt.decode(refreshToken) as jwt.JwtPayload; const userByKeycloakId = await this.userRepository.getUserByKeycloakId(data?.sub); const tokenResponse = await this.clientRegistrationService.getAccessToken(refreshToken, userByKeycloakId?.['clientId'], userByKeycloakId?.['clientSecret']); return tokenResponse; diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 6452092e0..4e748bf64 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -150,6 +150,14 @@ export enum PromiseResult { export enum PrismaTables { PRESENTATIONS = 'presentations', CREDENTIALS = 'credentials', + ORG_AGENTS = 'org_agents', + ORG_DIDS = 'org_dids', + AGENT_INVITATIONS = 'agent_invitations', + CONNECTIONS = 'connections', + ECOSYSTEM_INVITATIONS = 'ecosystem_invitations', + ECOSYSTEM_ORGS = 'ecosystem_orgs', + FILE_UPLOAD = 'file_upload', + NOTIFICATION = 'notification', } export enum IssuanceProcessState { diff --git a/libs/prisma-service/prisma/migrations/20240619102525_add_cascade_delete/migration.sql b/libs/prisma-service/prisma/migrations/20240619102525_add_cascade_delete/migration.sql new file mode 100644 index 000000000..e89ce2880 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240619102525_add_cascade_delete/migration.sql @@ -0,0 +1,11 @@ +-- DropForeignKey +ALTER TABLE "credential_definition" DROP CONSTRAINT "credential_definition_orgId_fkey"; + +-- DropForeignKey +ALTER TABLE "schema" DROP CONSTRAINT "schema_orgId_fkey"; + +-- AddForeignKey +ALTER TABLE "schema" ADD CONSTRAINT "schema_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "credential_definition" ADD CONSTRAINT "credential_definition_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/libs/prisma-service/prisma/migrations/20240619103315_allow_null_organization_id/migration.sql b/libs/prisma-service/prisma/migrations/20240619103315_allow_null_organization_id/migration.sql new file mode 100644 index 000000000..5e2140a14 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240619103315_allow_null_organization_id/migration.sql @@ -0,0 +1,14 @@ +-- DropForeignKey +ALTER TABLE "credential_definition" DROP CONSTRAINT "credential_definition_orgId_fkey"; + +-- DropForeignKey +ALTER TABLE "schema" DROP CONSTRAINT "schema_orgId_fkey"; + +-- AlterTable +ALTER TABLE "schema" ALTER COLUMN "orgId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "schema" ADD CONSTRAINT "schema_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organisation"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "credential_definition" ADD CONSTRAINT "credential_definition_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organisation"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 1b75aa584..ec3df2758 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -108,7 +108,7 @@ model organisation { registrationNumber String? @db.VarChar(100) country String? @db.VarChar(100) city String? @db.VarChar(100) - state String? @db.VarChar(100) + state String? @db.VarChar(100) connections connections[] credentials credentials[] org_agents org_agents[] @@ -267,8 +267,8 @@ model schema { schemaLedgerId String @db.VarChar publisherDid String @db.VarChar issuerId String @db.VarChar - orgId String @db.Uuid - organisation organisation @relation(fields: [orgId], references: [id]) + orgId String? @db.Uuid + organisation organisation? @relation(fields: [orgId], references: [id], onDelete: SetNull) ledgers ledgers? @relation(fields: [ledgerId], references: [id]) ledgerId String? @db.Uuid type String? @db.VarChar @@ -286,7 +286,7 @@ model credential_definition { schemaLedgerId String @db.VarChar schemaId String @db.Uuid revocable Boolean @default(false) - organisation organisation? @relation(fields: [orgId], references: [id]) + organisation organisation? @relation(fields: [orgId], references: [id], onDelete: SetNull) orgId String? @db.Uuid schema schema @relation(fields: [schemaId], references: [id]) }