Skip to content

Commit

Permalink
feat: delete connection records (#782)
Browse files Browse the repository at this point in the history
Signed-off-by: bhavanakarwade <bhavana.karwade@ayanworks.com>
Signed-off-by: KulkarniShashank <shashank.kulkarni@ayanworks.com>
  • Loading branch information
bhavanakarwade authored and KulkarniShashank committed Sep 12, 2024
1 parent 08db654 commit d0a573c
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 29 deletions.
32 changes: 30 additions & 2 deletions apps/api-gateway/src/connection/connection.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { 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 } from '@nestjs/common';
import { Controller, Post, Logger, Body, UseGuards, HttpStatus, Res, Get, Param, UseFilters, Query, Inject, ParseUUIDPipe, BadRequestException, Delete } 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';
Expand All @@ -22,7 +22,8 @@ import { IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.inter
import { SortFields } from 'apps/connection/src/enum/connection.enum';
import { ClientProxy} from '@nestjs/microservices';
import { QuestionAnswerWebhookDto, QuestionDto} from './dtos/question-answer.dto';

// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { user } from '@prisma/client';
@UseFilters(CustomExceptionFilter)
@Controller()
@ApiTags('connections')
Expand Down Expand Up @@ -330,4 +331,31 @@ export class ConnectionController {
}
return res.status(HttpStatus.CREATED).json(finalResponse);
}

@Delete('/:orgId/connections')
@ApiOperation({ summary: 'Delete connection record', description: 'Delete connections by orgId' })
@ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto })
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async deleteConnectionsByOrgId(
@Param(
'orgId',
new ParseUUIDPipe({
exceptionFactory: (): Error => {
throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId);
}
})
)
orgId: string,
@User() user: user,
@Res() res: Response
): Promise<Response> {
await this.connectionService.deleteConnectionRecords(orgId, user);
const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: ResponseMessages.connection.success.deleteConnectionRecord
};
return res.status(HttpStatus.OK).json(finalResponse);
}
}
9 changes: 7 additions & 2 deletions apps/api-gateway/src/connection/connection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ 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 } from '@credebl/common/interfaces/connection.interface';
import { IConnectionList, IDeletedConnectionsRecord } from '@credebl/common/interfaces/connection.interface';
import { AgentConnectionSearchCriteria, IConnectionDetailsById, IConnectionSearchCriteria } from '../interfaces/IConnectionSearch.interface';
import { QuestionDto } from './dtos/question-answer.dto';

import { user } from '@prisma/client';
@Injectable()
export class ConnectionService extends BaseService {
constructor(@Inject('NATS_CLIENT') private readonly connectionServiceProxy: ClientProxy) {
Expand Down Expand Up @@ -156,4 +156,9 @@ export class ConnectionService extends BaseService {
const payload = { user, createOutOfBandConnectionInvitation };
return this.sendNatsMessage(this.connectionServiceProxy, 'create-connection-invitation', payload);
}

async deleteConnectionRecords(orgId: string, userDetails: user): Promise<IDeletedConnectionsRecord> {
const payload = { orgId, userDetails };
return this.sendNatsMessage(this.connectionServiceProxy, 'delete-connection-records', payload);
}
}
13 changes: 0 additions & 13 deletions apps/api-gateway/src/connection/enums/connections.enum.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
export enum Connections {
start = 'start',
invitationSent = 'invitation-sent',
invitationReceived = 'invitation-received',
requestSent = 'request-sent',
declined = 'decliend',
requestReceived = 'request-received',
responseSent = 'response-sent',
responseReceived = 'response-received',
complete = 'complete',
abandoned = 'abandoned'
}

export declare enum HandshakeProtocol {
Connections = "https://didcomm.org/connections/1.0",
DidExchange = "https://didcomm.org/didexchange/1.0"
Expand Down
10 changes: 8 additions & 2 deletions apps/connection/src/connection.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import {
IReceiveInvitationByUrlOrg,
IReceiveInvitationResponse
} from './interfaces/connection.interfaces';
import { IConnectionList } from '@credebl/common/interfaces/connection.interface';
import { IConnectionList, IDeletedConnectionsRecord } from '@credebl/common/interfaces/connection.interface';
import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface';
import { IQuestionPayload } from './interfaces/question-answer.interfaces';

import { user } from '@prisma/client';
@Controller()
export class ConnectionController {
constructor(private readonly connectionService: ConnectionService) {}
Expand Down Expand Up @@ -95,4 +95,10 @@ export class ConnectionController {
async createConnectionInvitation(payload: ICreateOutOfbandConnectionInvitation): Promise<object> {
return this.connectionService.createConnectionInvitation(payload);
}

@MessagePattern({ cmd: 'delete-connection-records' })
async deleteConnectionRecords(payload: {orgId: string, userDetails: user}): Promise<IDeletedConnectionsRecord> {
const { orgId, userDetails } = payload;
return this.connectionService.deleteConnectionRecords(orgId, userDetails);
}
}
3 changes: 2 additions & 1 deletion apps/connection/src/connection.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ConnectionRepository } from './connection.repository';
import { PrismaService } from '@credebl/prisma-service';
import { CacheModule } from '@nestjs/cache-manager';
import { getNatsOptions } from '@credebl/common/nats.config';
import { UserActivityRepository } from 'libs/user-activity/repositories';
// import { nkeyAuthenticator } from 'nats';

@Module({
Expand All @@ -24,6 +25,6 @@ import { getNatsOptions } from '@credebl/common/nats.config';
CacheModule.register()
],
controllers: [ConnectionController],
providers: [ConnectionService, ConnectionRepository, PrismaService, Logger]
providers: [ConnectionService, ConnectionRepository, UserActivityRepository, PrismaService, Logger]
})
export class ConnectionModule { }
52 changes: 50 additions & 2 deletions apps/connection/src/connection.repository.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Injectable, Logger } from '@nestjs/common';
import { Injectable, Logger, ConflictException } from '@nestjs/common';
import { PrismaService } from '@credebl/prisma-service';
// eslint-disable-next-line camelcase
import { agent_invitations, org_agents, platform_config, shortening_url } from '@prisma/client';
import { IConnectionSearchCriteria, ICreateConnection, OrgAgent } from './interfaces/connection.interfaces';
import { IUserRequest } from '@credebl/user-request/user-request.interface';
import { IConnectionsListCount } from '@credebl/common/interfaces/connection.interface';
import { IConnectionsListCount, IDeletedConnectionsRecord } from '@credebl/common/interfaces/connection.interface';
import { SortValue } from '@credebl/enum/enum';
// import { OrgAgent } from './interfaces/connection.interfaces';
@Injectable()
Expand Down Expand Up @@ -316,4 +316,52 @@ export class ConnectionRepository {
throw error;
}
}

async deleteConnectionRecordsByOrgId(orgId: string): Promise<IDeletedConnectionsRecord> {
const tablesToCheck = ['credentials', 'presentations'];

try {
return await this.prisma.$transaction(async (prisma) => {
const referenceCounts = await Promise.all(
tablesToCheck.map((table) => prisma[table].count({ where: { orgId } }))
);

const referencedTables = referenceCounts
.map((count, index) => (0 < count ? tablesToCheck[index] : null))
.filter(Boolean);

if (0 < referencedTables.length) {
throw new ConflictException(`Organization ID ${orgId} is referenced in the following table(s): ${referencedTables.join(', ')}`);
}

const getConnectionRecords = await prisma.connections.findMany(
{
where: {
orgId
},
select: {
createDateTime: true,
createdBy: true,
connectionId: true,
theirLabel: true,
state: true,
orgId: true

}
});

const deleteConnectionRecords = await prisma.connections.deleteMany(
{
where: {
orgId
}
});

return {getConnectionRecords, deleteConnectionRecords };
});
} catch (error) {
this.logger.error(`Error in deleting connection records: ${error.message}`);
throw error;
}
}
}
55 changes: 51 additions & 4 deletions apps/connection/src/connection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,21 @@ import {
import { ConnectionRepository } from './connection.repository';
import { ResponseMessages } from '@credebl/common/response-messages';
import { IUserRequest } from '@credebl/user-request/user-request.interface';
import { OrgAgentType } from '@credebl/enum/enum';
import { platform_config } from '@prisma/client';
import { IConnectionList, ICreateConnectionUrl } from '@credebl/common/interfaces/connection.interface';
import { OrgAgentType, ConnectionProcessState } from '@credebl/enum/enum';
import { Cache } from 'cache-manager';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { IConnectionList, ICreateConnectionUrl, IDeletedConnectionsRecord } from '@credebl/common/interfaces/connection.interface';
import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnectionSearch.interface';
import { IQuestionPayload } from './interfaces/question-answer.interfaces';

import { RecordType, user } from '@prisma/client';
import { UserActivityRepository } from 'libs/user-activity/repositories';
@Injectable()
export class ConnectionService {
constructor(
private readonly commonService: CommonService,
@Inject('NATS_CLIENT') private readonly connectionServiceProxy: ClientProxy,
private readonly connectionRepository: ConnectionRepository,
private readonly userActivityRepository: UserActivityRepository,
private readonly logger: Logger,
@Inject(CACHE_MANAGER) private cacheService: Cache
) {}
Expand Down Expand Up @@ -833,4 +836,48 @@ export class ConnectionService {
throw new RpcException(error.response ? error.response : error);
}
}

async deleteConnectionRecords(orgId: string, user: user): Promise<IDeletedConnectionsRecord> {
try {
const deleteConnections = await this.connectionRepository.deleteConnectionRecordsByOrgId(orgId);

if (0 === deleteConnections?.deleteConnectionRecords?.count) {
throw new NotFoundException(ResponseMessages.connection.error.connectionRecordNotFound);
}

const statusCounts = {
[ConnectionProcessState.START]: 0,
[ConnectionProcessState.COMPLETE]: 0,
[ConnectionProcessState.ABANDONED]: 0,
[ConnectionProcessState.INVITATION_SENT]: 0,
[ConnectionProcessState.INVITATION_RECEIVED]: 0,
[ConnectionProcessState.REQUEST_SENT]: 0,
[ConnectionProcessState.DECLIEND]: 0,
[ConnectionProcessState.REQUEST_RECEIVED]: 0,
[ConnectionProcessState.RESPONSE_SENT]: 0,
[ConnectionProcessState.RESPONSE_RECEIVED]: 0
};

await Promise.all(deleteConnections.getConnectionRecords.map(async (record) => {
statusCounts[record.state]++;
}));

const filteredStatusCounts = Object.fromEntries(
Object.entries(statusCounts).filter(entry => 0 < entry[1])
);

const deletedConnectionData = {
deletedConnectionsRecordsCount: deleteConnections?.deleteConnectionRecords?.count,
deletedRecordsStatusCount: filteredStatusCounts
};

await this.userActivityRepository._orgDeletedActivity(orgId, user, deletedConnectionData, RecordType.CONNECTION);

return deleteConnections;

} catch (error) {
this.logger.error(`[deleteConnectionRecords] - error in deleting connection records: ${JSON.stringify(error)}`);
throw new RpcException(error.response ? error.response : error);
}
}
}
8 changes: 7 additions & 1 deletion libs/common/src/interfaces/connection.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@ export interface IConnectionsListCount {
recipientKey?:string;
invitationDid?: string
}


export interface IDeletedConnectionsRecord {
getConnectionRecords: IConnectionItem[];
deleteConnectionRecords: {
count: number;
};
}
6 changes: 4 additions & 2 deletions libs/common/src/response-messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,15 @@ export const ResponseMessages = {
fetchConnection: 'Connection details fetched successfully',
fetch: 'Connections details fetched successfully',
questionAnswerRecord: 'Question Answer record fetched successfully',
questionSend:'Question sent successfully'
questionSend:'Question sent successfully',
deleteConnectionRecord: 'Connection records deleted'
},
error: {
exists: 'Connection is already exist',
connectionNotFound: 'Connection not found',
agentEndPointNotFound: 'agentEndPoint Not Found',
agentUrlNotFound: 'agent url not found'
agentUrlNotFound: 'agent url not found',
connectionRecordNotFound: 'Connection records not found'
}
},
issuance: {
Expand Down

0 comments on commit d0a573c

Please sign in to comment.