diff --git a/apps/api-gateway/src/ecosystem/dtos/get-all-endorsements.dto.ts b/apps/api-gateway/src/ecosystem/dtos/get-all-endorsements.dto.ts new file mode 100644 index 000000000..a0878bf15 --- /dev/null +++ b/apps/api-gateway/src/ecosystem/dtos/get-all-endorsements.dto.ts @@ -0,0 +1,35 @@ + +import { Transform, Type } from 'class-transformer'; +import { toNumber } from '@credebl/common/cast.helper'; + +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsOptional, IsString } from 'class-validator'; +import { EndorserTransactionType } from '@credebl/enum/enum'; + +export class GetAllEndorsementsDto { + @ApiProperty({ required: false, default: 1 }) + @IsOptional() + @Type(() => Number) + @Transform(({ value }) => toNumber(value)) + pageNumber = 1; + + @ApiProperty({ required: false }) + @IsOptional() + @IsString() + @Type(() => String) + search = ''; + + @ApiProperty({ required: false }) + @IsOptional() + @Type(() => Number) + @Transform(({ value }) => toNumber(value)) + pageSize = 10; + + @ApiProperty({ + enum: [EndorserTransactionType.SCHEMA, EndorserTransactionType.CREDENTIAL_DEFINITION] + }) + @IsOptional() + @IsEnum(EndorserTransactionType) + type: EndorserTransactionType.SCHEMA | EndorserTransactionType.CREDENTIAL_DEFINITION; + +} diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index cca833bf4..c62b87ce6 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -27,6 +27,7 @@ import { EcosystemRolesGuard } from '../authz/guards/ecosystem-roles.guard'; import { EcosystemsRoles, Roles } from '../authz/decorators/roles.decorator'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; import { OrgRoles } from 'libs/org-roles/enums'; +import { GetAllEndorsementsDto } from './dtos/get-all-endorsements.dto'; import { CreateEcosystemDto } from './dtos/create-ecosystem-dto'; @@ -104,6 +105,44 @@ export class EcosystemController { } + @Get('/:ecosystemId/:orgId/endorsement-transactions') + @ApiOperation({ summary: 'Get all endorsement transactions', description: 'Get all endorsement transactions' }) + @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard, OrgRolesGuard) + @ApiBearerAuth() + @EcosystemsRoles(EcosystemRoles.ECOSYSTEM_OWNER, EcosystemRoles.ECOSYSTEM_LEAD, EcosystemRoles.ECOSYSTEM_MEMBER) + @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER) + @ApiQuery({ + name: 'pageNumber', + type: Number, + required: false + }) + @ApiQuery({ + name: 'pageSize', + type: Number, + required: false + }) + @ApiQuery({ + name: 'search', + type: String, + required: false + }) + async getEndorsementTranasactions( + @Param('ecosystemId') ecosystemId: string, + @Param('orgId') orgId: string, + @Query() getAllEndorsementsDto: GetAllEndorsementsDto, + @Res() res: Response + ): Promise { + const ecosystemList = await this.ecosystemService.getEndorsementTranasactions(ecosystemId, orgId, getAllEndorsementsDto); + const finalResponse: IResponseType = { + statusCode: HttpStatus.OK, + message: `Endorser transactions fetched successfully`, + data: ecosystemList.response + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + @Get('/:ecosystemId/:orgId/invitations') @ApiOperation({ summary: 'Get all sent invitations', description: 'Get all sent invitations' }) @ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto }) diff --git a/apps/api-gateway/src/ecosystem/ecosystem.service.ts b/apps/api-gateway/src/ecosystem/ecosystem.service.ts index b3bf387fa..d8416fca0 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.service.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.service.ts @@ -6,6 +6,7 @@ import { BulkEcosystemInvitationDto } from './dtos/send-invitation.dto'; import { AcceptRejectEcosystemInvitationDto } from './dtos/accept-reject-ecosysteminvitation-dto'; import { GetAllEcosystemInvitationsDto } from './dtos/get-all-sent-invitations.dto'; import { GetAllSentEcosystemInvitationsDto } from './dtos/get-all-sent-ecosystemInvitations-dto'; +import { GetAllEndorsementsDto } from './dtos/get-all-endorsements.dto'; import { RequestSchemaDto } from './dtos/request-schema-dto'; @@ -99,6 +100,16 @@ export class EcosystemService extends BaseService { return this.sendNats(this.serviceProxy, 'fetch-ecosystem-org-data', payload); } + async getEndorsementTranasactions( + ecosystemId: string, + orgId: string, + getAllEndorsements: GetAllEndorsementsDto + ): Promise<{ response: object }> { + const { pageNumber, pageSize, search, type } = getAllEndorsements; + const payload = { ecosystemId, orgId, pageNumber, pageSize, search, type }; + return this.sendNats(this.serviceProxy, 'get-endorsement-transactions', payload); + } + async schemaEndorsementRequest(requestSchemaPayload: RequestSchemaDto, orgId: number): Promise { const payload = { requestSchemaPayload, orgId}; diff --git a/apps/ecosystem/interfaces/endorsements.interface.ts b/apps/ecosystem/interfaces/endorsements.interface.ts new file mode 100644 index 000000000..d4c837c06 --- /dev/null +++ b/apps/ecosystem/interfaces/endorsements.interface.ts @@ -0,0 +1,9 @@ +export interface GetEndorsementsPayload { + ecosystemId: string; + orgId: string; + status: string; + pageNumber: number; + pageSize: number; + search: string; + type: string; + } \ No newline at end of file diff --git a/apps/ecosystem/src/ecosystem.controller.ts b/apps/ecosystem/src/ecosystem.controller.ts index 17960c4a1..f12ea629f 100644 --- a/apps/ecosystem/src/ecosystem.controller.ts +++ b/apps/ecosystem/src/ecosystem.controller.ts @@ -6,6 +6,7 @@ import { Body } from '@nestjs/common'; import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { AcceptRejectEcosystemInvitationDto } from '../dtos/accept-reject-ecosysteminvitation.dto'; import { FetchInvitationsPayload } from '../interfaces/invitations.interface'; +import { GetEndorsementsPayload } from '../interfaces/endorsements.interface'; import { RequestSchemaEndorsement } from '../interfaces/ecosystem.interfaces'; @Controller() @@ -97,14 +98,15 @@ export class EcosystemController { ); } - @MessagePattern({ cmd: 'fetch-ecosystem-org-data' }) - async fetchEcosystemOrg( - @Body() payload: { ecosystemId: string, orgId: string} + @MessagePattern({ cmd: 'get-endorsement-transactions' }) + async getEndorsementTransactions( + @Body() payload: GetEndorsementsPayload ): Promise { - return this.ecosystemService.fetchEcosystemOrg( + return this.ecosystemService.getEndorsementTransactions( payload ); - } + } + /** * diff --git a/apps/ecosystem/src/ecosystem.repository.ts b/apps/ecosystem/src/ecosystem.repository.ts index d5dad6814..0de7ad1f0 100644 --- a/apps/ecosystem/src/ecosystem.repository.ts +++ b/apps/ecosystem/src/ecosystem.repository.ts @@ -336,7 +336,48 @@ export class EcosystemRepository { } - /** + async getEndorsementsWithPagination(queryObject: object, pageNumber: number, pageSize: number): Promise { + try { + const result = await this.prisma.$transaction([ + this.prisma.endorsement_transaction.findMany({ + where: { + ...queryObject + }, + select:{ + id:true, + endorserDid: true, + authorDid: true, + status: true, + type: true, + ecosystemOrgs: true + }, + take: pageSize, + skip: (pageNumber - 1) * pageSize, + orderBy: { + createDateTime: 'desc' + } + }), + this.prisma.endorsement_transaction.count({ + where: { + ...queryObject + } + }) + ]); + + // eslint-disable-next-line prefer-destructuring + const transactions = result[0]; + // eslint-disable-next-line prefer-destructuring + const totalCount = result[1]; + const totalPages = Math.ceil(totalCount / pageSize); + + return { totalPages, transactions }; + } catch (error) { + this.logger.error(`error: ${JSON.stringify(error)}`); + throw new InternalServerErrorException(error); + } + } + +/** * Description: Get getAgentEndPoint by orgId * @param orgId * @returns Get getAgentEndPoint details @@ -419,6 +460,7 @@ export class EcosystemRepository { throw new InternalServerErrorException(error); } } + // eslint-disable-next-line camelcase async getEcosystemOrgDetailsbyId(orgId: string): Promise { try { @@ -481,5 +523,4 @@ export class EcosystemRepository { } } - } \ No newline at end of file diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 2f2e358d7..f511d1283 100644 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -12,6 +12,7 @@ import { AcceptRejectEcosystemInvitationDto } from '../dtos/accept-reject-ecosys import { Invitation, OrgAgentType } from '@credebl/enum/enum'; import { EcosystemOrgStatus, EcosystemRoles } from '../enums/ecosystem.enum'; import { FetchInvitationsPayload } from '../interfaces/invitations.interface'; +import { GetEndorsementsPayload } from '../interfaces/endorsements.interface'; import { RequestSchemaEndorsement, SchemaMessage, SchemaTransactionPayload, SchemaTransactionResponse, SignedTransactionMessage } from '../interfaces/ecosystem.interfaces'; // eslint-disable-next-line camelcase import { platform_config } from '@prisma/client'; @@ -293,7 +294,7 @@ export class EcosystemService { endorserDid: ecosystemLeadAgentDetails.orgDid, authorDid: agentDetails.orgDid, requestPayload: schemaTransactionRequest.message.schemaState.schemaRequest, - status: "Requested", + status: 'Requested', ecosystemOrgId: getEcosystemOrgDetailsByOrgId.id }; return this.ecosystemRepository.storeTransactionRequest(schemaTransactionResponse); @@ -446,5 +447,30 @@ export class EcosystemService { return platformConfigData[0].enableEcosystem; } + async getEndorsementTransactions(payload: GetEndorsementsPayload): Promise { + const {ecosystemId, orgId, pageNumber, pageSize, search, type } = payload; + try { + + const query = { + ecosystemOrgs: { + ecosystemId, + orgId + }, + OR: [ + { status: { contains: search, mode: 'insensitive' } }, + { authorDid: { contains: search, mode: 'insensitive' } } + ] + }; + + if (type) { + query['type'] = type; + } + + return await this.ecosystemRepository.getEndorsementsWithPagination(query, pageNumber, pageSize); + } catch (error) { + this.logger.error(`In error getEndorsementTransactions: ${JSON.stringify(error)}`); + throw new InternalServerErrorException(error); + } + } } diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 94ac465ea..e47471927 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -19,6 +19,11 @@ export enum EcosystemRoles { ECOSYSTEM_OWNER = 'Ecosystem Owner' } +export enum EndorserTransactionType{ + SCHEMA = 'schema', + CREDENTIAL_DEFINITION = 'credential-definition', +} + export enum OrgAgentType { DEDICATED = 1, SHARED = 2 diff --git a/libs/prisma-service/prisma/data/credebl-master-table.json b/libs/prisma-service/prisma/data/credebl-master-table.json index a3966a6ca..5053c4d65 100644 --- a/libs/prisma-service/prisma/data/credebl-master-table.json +++ b/libs/prisma-service/prisma/data/credebl-master-table.json @@ -107,5 +107,43 @@ "registerDIDPayload": "", "indyNamespace": "indicio:testnet" } + ], + "endorseData": [ + { + "id": "0f8fad5b-d9cb-469f-a165-70867728950f", + "endorserDid": "endorser123", + "authorDid": "author456", + "requestPayload": "{\"type\": \"dummy_request_1\"}", + "responsePayload": "{\"type\": \"dummy_response_1\"}", + "status": "Requested", + "ecosystemOrgId": "1c247b4a-e2f6-48c0-8aa2-65ea47474294" + }, + { + "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", + "endorserDid": "endorser789", + "authorDid": "author101", + "requestPayload": "{\"type\": \"dummy_request_2\"}", + "responsePayload": "{\"type\": \"dummy_response_2\"}", + "status": "Signed", + "ecosystemOrgId": "1c247b4a-e2f6-48c0-8aa2-65ea47474294" + }, + { + "id": "a89b6e81-a1ff-4d13-a9e2-17176e707aac", + "endorserDid": "endorser321", + "authorDid": "author654", + "requestPayload": "{\"type\": \"dummy_request_3\"}", + "responsePayload": "{\"type\": \"dummy_response_3\"}", + "status": "Declined", + "ecosystemOrgId": "a2443e09-45be-4739-b8b3-0d4ffaecea94" + }, + { + "id": "f47ac10b-58cc-4372-a567-0e02b2c3d47c", + "endorserDid": "endorser999", + "authorDid": "author777", + "requestPayload": "{\"type\": \"dummy_request_4\"}", + "responsePayload": "{\"type\": \"dummy_response_4\"}", + "status": "Submitted", + "ecosystemOrgId": "ca6ee687-a3a9-42ce-9e49-02bf62f5c93a" + } ] } \ No newline at end of file diff --git a/libs/prisma-service/prisma/migrations/20231007125030_endorse_transaction_type_date/migration.sql b/libs/prisma-service/prisma/migrations/20231007125030_endorse_transaction_type_date/migration.sql new file mode 100644 index 000000000..472616297 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20231007125030_endorse_transaction_type_date/migration.sql @@ -0,0 +1,7 @@ +-- AlterTable +ALTER TABLE "endorsement_transaction" ADD COLUMN "createDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "createdBy" INTEGER NOT NULL DEFAULT 1, +ADD COLUMN "deletedAt" TIMESTAMP(6), +ADD COLUMN "lastChangedBy" INTEGER NOT NULL DEFAULT 1, +ADD COLUMN "lastChangedDateTime" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "type" TEXT; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 5637e59d0..45aa68dd9 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -382,12 +382,18 @@ model ecosystem_orgs { } model endorsement_transaction { - id String @id @default(uuid()) - endorserDid String - authorDid String - requestPayload String - responsePayload String - status String - ecosystemOrgId String - ecosystemOrgs ecosystem_orgs @relation(fields: [ecosystemOrgId], references: [id]) + id String @id @default(uuid()) + endorserDid String + authorDid String + requestPayload String + responsePayload String + type String? + createDateTime DateTime @default(now()) @db.Timestamptz(6) + createdBy Int @default(1) + lastChangedDateTime DateTime @default(now()) @db.Timestamptz(6) + lastChangedBy Int @default(1) + deletedAt DateTime? @db.Timestamp(6) + status String + ecosystemOrgId String + ecosystemOrgs ecosystem_orgs @relation(fields: [ecosystemOrgId], references: [id]) }