diff --git a/apps/api-gateway/src/ecosystem/dtos/accept-reject-invitations.dto.ts b/apps/api-gateway/src/ecosystem/dtos/accept-reject-invitations.dto.ts index 57fa80731..429ddfba2 100644 --- a/apps/api-gateway/src/ecosystem/dtos/accept-reject-invitations.dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/accept-reject-invitations.dto.ts @@ -1,4 +1,4 @@ -import { IsEnum, IsNotEmpty, IsString, MaxLength, MinLength} from 'class-validator'; +import { IsEnum, IsNotEmpty} from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { Invitation } from '@credebl/enum/enum'; @@ -11,20 +11,6 @@ export class AcceptRejectEcosystemInvitationDto { invitationId: string; orgId: string; - @ApiProperty() - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'organization name is required.' }) - @MinLength(2, { message: 'organization name must be at least 2 characters.' }) - @MaxLength(50, { message: 'organization name must be at most 50 characters.' }) - @IsString({ message: 'organization name must be in string format.' }) - orgName: string; - - @ApiProperty() - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'organization did is required.' }) - @IsString({ message: 'organization did must be in string format.' }) - orgDid: string; - @ApiProperty({ enum: [Invitation.ACCEPTED, Invitation.REJECTED] }) 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 1d57ee8b0..6c8d5d9a0 100644 --- a/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts +++ b/apps/api-gateway/src/ecosystem/dtos/create-ecosystem-dto.ts @@ -1,5 +1,5 @@ import { ApiExtraModels, ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsBoolean, IsInt, IsNotEmpty, IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; +import { IsBoolean, IsNotEmpty, IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; import { Transform } from 'class-transformer'; import { trim } from '@credebl/common/cast.helper'; @@ -39,20 +39,6 @@ export class CreateEcosystemDto { @IsString({ message: 'logo must be in string format.' }) logo?: string; - @ApiProperty() - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'organization name is required.' }) - @MinLength(2, { message: 'organization name must be at least 2 characters.' }) - @MaxLength(50, { message: 'organization name must be at most 50 characters.' }) - @IsString({ message: 'organization name must be in string format.' }) - orgName: string; - - @ApiProperty() - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'organization did is required.' }) - @IsString({ message: 'organization did must be in string format.' }) - orgDid: string; - @ApiPropertyOptional({ example: 'false' }) @IsBoolean() @IsOptional() diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index 6cb7d6219..9a8a6715b 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -122,7 +122,7 @@ export class EcosystemController { @ApiOperation({ summary: 'Get all organization ecosystems', description: 'Get all existing ecosystems of an specific organization' }) @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) @ApiBearerAuth() async getEcosystem( @Param('orgId') orgId: string, @@ -141,7 +141,7 @@ export class EcosystemController { @ApiOperation({ summary: 'Get ecosystem dashboard details', description: 'Get ecosystem dashboard details' }) @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard, EcosystemRolesGuard) - @Roles(OrgRoles.OWNER, OrgRoles.ADMIN) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) @EcosystemsRoles(EcosystemRoles.ECOSYSTEM_OWNER, EcosystemRoles.ECOSYSTEM_LEAD, EcosystemRoles.ECOSYSTEM_MEMBER) @ApiBearerAuth() async getEcosystemDashboardDetails(@Param('ecosystemId') ecosystemId: string, @Param('orgId') orgId: string, @Res() res: Response): Promise { diff --git a/apps/ecosystem/dtos/accept-reject-ecosysteminvitation.dto.ts b/apps/ecosystem/dtos/accept-reject-ecosysteminvitation.dto.ts index cf885b6cb..1bc76de4f 100644 --- a/apps/ecosystem/dtos/accept-reject-ecosysteminvitation.dto.ts +++ b/apps/ecosystem/dtos/accept-reject-ecosysteminvitation.dto.ts @@ -4,7 +4,7 @@ export class AcceptRejectEcosystemInvitationDto { orgId: string; invitationId: string; status: Invitation; - orgName: string; - orgDid: string; + orgName?: string; + orgDid?: string; userId?: string; } diff --git a/apps/ecosystem/dtos/update-ecosystemOrgs.dto.ts b/apps/ecosystem/dtos/update-ecosystemOrgs.dto.ts index d8e1dd41e..c1d431a77 100644 --- a/apps/ecosystem/dtos/update-ecosystemOrgs.dto.ts +++ b/apps/ecosystem/dtos/update-ecosystemOrgs.dto.ts @@ -1,7 +1,5 @@ export class updateEcosystemOrgsDto { orgId: string; - orgName: string; - orgDid: string; status: string; ecosystemId: string; ecosystemRoleId: string; diff --git a/apps/ecosystem/interfaces/ecosystem.interfaces.ts b/apps/ecosystem/interfaces/ecosystem.interfaces.ts index e90e623f5..bb3e8b94f 100644 --- a/apps/ecosystem/interfaces/ecosystem.interfaces.ts +++ b/apps/ecosystem/interfaces/ecosystem.interfaces.ts @@ -195,4 +195,34 @@ export interface CreateEcosystem { orgId?: string; autoEndorsement: boolean +} + +export interface OrganizationData { + id: string; + createDateTime: string; + createdBy: string; + lastChangedDateTime: string; + lastChangedBy: string; + name: string; + description: string; + orgSlug: string; + logoUrl: string; + website: string; + publicProfile: boolean; + org_agents: OrgAgent[]; +} + +export interface OrgAgent { + id: string; + orgDid: string; + verkey: string; + agentEndPoint: string; + isDidPublic: boolean; + agentSpinUpStatus: number; + walletName: string; + tenantId: string; + agentsTypeId: string; + orgId: string; + orgAgentTypeId: string; + ledgerId: string; } \ No newline at end of file diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index 4dae01570..4f3a08866 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -28,7 +28,7 @@ export class EcosystemRepository { async createNewEcosystem(createEcosystemDto): Promise { try { const transaction = await this.prisma.$transaction(async (prisma) => { - const { name, description, userId, logo, tags, orgId, orgName, orgDid, autoEndorsement } = createEcosystemDto; + const { name, description, userId, logo, tags, orgId, autoEndorsement } = createEcosystemDto; const createdEcosystem = await prisma.ecosystem.create({ data: { name, @@ -64,8 +64,6 @@ export class EcosystemRepository { status: EcosystemOrgStatus.ACTIVE, ecosystemId: createdEcosystem.id, ecosystemRoleId: ecosystemRoleDetails.id, - orgName, - orgDid, deploymentMode: DeploymentModeType.PROVIDER_HOSTED, createdBy: userId, lastChangedBy: userId @@ -367,7 +365,7 @@ export class EcosystemRepository { // eslint-disable-next-line camelcase async updateEcosystemOrgs(createEcosystemOrgsDto: updateEcosystemOrgsDto): Promise { try { - const { orgId, status, ecosystemRoleId, ecosystemId, orgName, orgDid, createdBy, lastChangedBy } = createEcosystemOrgsDto; + const { orgId, status, ecosystemRoleId, ecosystemId, createdBy, lastChangedBy } = createEcosystemOrgsDto; return this.prisma.ecosystem_orgs.create({ data: { @@ -375,8 +373,6 @@ export class EcosystemRepository { ecosystemId, status, ecosystemRoleId, - orgName, - orgDid, deploymentMode: DeploymentModeType.PROVIDER_HOSTED, createdBy, lastChangedBy @@ -449,10 +445,10 @@ export class EcosystemRepository { try { const query = { ecosystemId, - OR: - [{ orgId: { contains: search, mode: 'insensitive' } }] + OR: [{ organisation: { name: { contains: search, mode: 'insensitive' } } }] }; return await this.getEcosystemMembersPagination(query, pageNumber, pageSize); + } catch (error) { this.logger.error(`error: ${JSON.stringify(error)}`); throw new InternalServerErrorException(error); @@ -468,7 +464,16 @@ export class EcosystemRepository { }, include: { ecosystem: true, - ecosystemRole: true + ecosystemRole: true, + organisation: { + select:{ + name: true, + orgSlug: true, + // eslint-disable-next-line camelcase + org_agents: true + + } + } }, take: pageSize, skip: (pageNumber - 1) * pageSize, @@ -545,7 +550,7 @@ export class EcosystemRepository { select: { ecosystem: true, ecosystemRole: true, - orgName: true + organisation: true } }); @@ -565,7 +570,16 @@ export class EcosystemRepository { authorDid: true, status: true, type: true, - ecosystemOrgs: true, + ecosystemOrgs: { + include:{ + organisation: { + select:{ + name: true, + orgSlug: true + } + } + } + }, requestPayload: true, responsePayload: true, createDateTime: true, diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 16fb17585..5e213796f 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -14,7 +14,7 @@ import { EcosystemConfigSettings, Invitation, OrgAgentType } from '@credebl/enum import { EcosystemOrgStatus, EcosystemRoles, endorsementTransactionStatus, endorsementTransactionType } from '../enums/ecosystem.enum'; import { FetchInvitationsPayload } from '../interfaces/invitations.interface'; import { EcosystemMembersPayload } from '../interfaces/ecosystemMembers.interface'; -import { CreateEcosystem, CredDefMessage, RequestCredDeffEndorsement, RequestSchemaEndorsement, SaveSchema, SchemaMessage, SignedTransactionMessage, saveCredDef, submitTransactionPayload } from '../interfaces/ecosystem.interfaces'; +import { CreateEcosystem, CredDefMessage, OrganizationData, RequestCredDeffEndorsement, RequestSchemaEndorsement, SaveSchema, SchemaMessage, SignedTransactionMessage, saveCredDef, submitTransactionPayload } from '../interfaces/ecosystem.interfaces'; import { GetAllSchemaList, GetEndorsementsPayload } from '../interfaces/endorsements.interface'; import { CommonConstants } from '@credebl/common/common.constant'; // eslint-disable-next-line camelcase @@ -58,6 +58,17 @@ export class EcosystemService { } } } + + const orgDetails: OrganizationData = await this.getOrganizationDetails(createEcosystemDto.orgId, createEcosystemDto.userId); + + if (!orgDetails) { + throw new NotFoundException(ResponseMessages.ecosystem.error.orgNotExist); + } + + if (0 === orgDetails.org_agents.length) { + throw new NotFoundException(ResponseMessages.ecosystem.error.orgDidNotExist); + } + const createEcosystem = await this.ecosystemRepository.createNewEcosystem(createEcosystemDto); if (!createEcosystem) { throw new NotFoundException(ResponseMessages.ecosystem.error.notCreated); @@ -65,6 +76,27 @@ export class EcosystemService { return createEcosystem; } + async getOrganizationDetails(orgId: string, userId: string): Promise { + const pattern = { cmd: 'get-organization-by-id' }; + const payload = { orgId, userId }; + + const orgData = await this.ecosystemServiceProxy + .send(pattern, payload) + .toPromise() + .catch((error) => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + throw new HttpException( + { + status: error.status, + error: error.message + }, + error.status + ); + }); + + return orgData; + } + /** * @@ -123,7 +155,7 @@ export class EcosystemService { endorsementsCount: endorseMemberCount.endorsementsCount, ecosystemLead: { role: ecosystemDetails['ecosystemRole']['name'], - orgName: ecosystemDetails['orgName'], + orgName: ecosystemDetails['organisation']['name'], config: endorseMemberCount.ecosystemConfigData } }; @@ -214,13 +246,23 @@ export class EcosystemService { }; } - const { orgId, status, invitationId, orgName, orgDid, userId } = acceptRejectInvitation; + const { orgId, status, invitationId, userId } = acceptRejectInvitation; const invitation = await this.ecosystemRepository.getEcosystemInvitationById(invitationId); if (!invitation) { throw new NotFoundException(ResponseMessages.ecosystem.error.invitationNotFound); } + const orgDetails: OrganizationData = await this.getOrganizationDetails(acceptRejectInvitation.orgId, acceptRejectInvitation.userId); + + if (!orgDetails) { + throw new NotFoundException(ResponseMessages.ecosystem.error.orgNotExist); + } + + if (0 === orgDetails.org_agents.length) { + throw new NotFoundException(ResponseMessages.ecosystem.error.orgDidNotExist); + } + const updatedInvitation = await this.updateEcosystemInvitation(invitationId, orgId, status); if (!updatedInvitation) { throw new NotFoundException(ResponseMessages.ecosystem.error.invitationNotUpdate); @@ -231,7 +273,7 @@ export class EcosystemService { } const ecosystemRole = await this.ecosystemRepository.getEcosystemRole(EcosystemRoles.ECOSYSTEM_MEMBER); - const updateEcosystemOrgs = await this.updatedEcosystemOrgs(orgId, orgName, orgDid, invitation.ecosystemId, ecosystemRole.id, userId); + const updateEcosystemOrgs = await this.updatedEcosystemOrgs(orgId, invitation.ecosystemId, ecosystemRole.id, userId); if (!updateEcosystemOrgs) { throw new NotFoundException(ResponseMessages.ecosystem.error.orgsNotUpdate); @@ -244,15 +286,13 @@ export class EcosystemService { } } - async updatedEcosystemOrgs(orgId: string, orgName: string, orgDid: string, ecosystemId: string, ecosystemRoleId: string, userId: string): Promise { + async updatedEcosystemOrgs(orgId: string, ecosystemId: string, ecosystemRoleId: string, userId: string): Promise { try { const data: updateEcosystemOrgsDto = { orgId, status: EcosystemOrgStatus.ACTIVE, ecosystemId, ecosystemRoleId, - orgName, - orgDid, createdBy: userId, lastChangedBy: userId }; diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 22606bf8c..a082f4e1c 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -58,7 +58,7 @@ export const ResponseMessages = { getOrgDashboard: 'Organization dashboard details fetched', getOrganizations: 'Organizations details fetched successfully', updateUserRoles: 'User roles updated successfully', - delete: 'Organization deleted successfully', + delete: 'Organization deleted successfully' }, error: { exists: 'An organization name is already exist', @@ -235,6 +235,8 @@ export const ResponseMessages = { }, error: { notCreated: 'Error while creating ecosystem', + 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', diff --git a/libs/prisma-service/prisma/migrations/20231127132350_map_ecosystem_orgs/migration.sql b/libs/prisma-service/prisma/migrations/20231127132350_map_ecosystem_orgs/migration.sql new file mode 100644 index 000000000..f0ff9d370 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20231127132350_map_ecosystem_orgs/migration.sql @@ -0,0 +1,16 @@ +/* + Warnings: + + - You are about to drop the column `orgDid` on the `ecosystem_orgs` table. All the data in the column will be lost. + - You are about to drop the column `orgName` on the `ecosystem_orgs` table. All the data in the column will be lost. + - Changed the type of `orgId` on the `ecosystem_orgs` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required. + +*/ +-- AlterTable +ALTER TABLE "ecosystem_orgs" DROP COLUMN "orgDid", +DROP COLUMN "orgName", +DROP COLUMN "orgId", +ADD COLUMN "orgId" UUID NOT NULL; + +-- AddForeignKey +ALTER TABLE "ecosystem_orgs" ADD CONSTRAINT "ecosystem_orgs_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organisation"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 24e1ea358..a5ea40211 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -101,6 +101,7 @@ model organisation { agent_invitations agent_invitations[] credential_definition credential_definition[] file_upload file_upload[] + ecosystemOrgs ecosystem_orgs[] } model org_invitations { @@ -371,9 +372,7 @@ model ecosystem_users { model ecosystem_orgs { id String @id @default(uuid()) @db.Uuid - orgId String - orgName String? - orgDid String? @db.VarChar + orgId String @db.Uuid status String deploymentMode String? ecosystemId String @db.Uuid @@ -386,6 +385,7 @@ model ecosystem_orgs { ecosystem ecosystem @relation(fields: [ecosystemId], references: [id]) ecosystemRole ecosystem_roles @relation(fields: [ecosystemRoleId], references: [id]) endorsementTransaction endorsement_transaction[] + organisation organisation? @relation(fields: [orgId], references: [id]) } model endorsement_transaction { diff --git a/libs/prisma-service/prisma/seed.ts b/libs/prisma-service/prisma/seed.ts index e81b282e3..e6513e575 100644 --- a/libs/prisma-service/prisma/seed.ts +++ b/libs/prisma-service/prisma/seed.ts @@ -101,26 +101,26 @@ const createPlatformUserOrgRoles = async (): Promise => { where: { email: `${CommonConstants.PLATFORM_ADMIN_EMAIL}` } - }) + }); const orgId = await prisma.organisation.findFirst({ where: { name: `${CommonConstants.PLATFORM_ADMIN_ORG}` } - }) + }); const orgRoleId = await prisma.org_roles.findUnique({ where: { name: `${CommonConstants.PLATFORM_ADMIN_ORG_ROLE}` } - }) + }); const platformOrganization = await prisma.user_org_roles.create({ data: { userId: userId.id, orgRoleId: orgRoleId.id, orgId: orgId.id - }, + } }); logger.log(platformOrganization);