Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: delete issuance records #781

Merged
merged 5 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions apps/api-gateway/src/issuance/enums/Issuance.enum.ts
Original file line number Diff line number Diff line change
@@ -1,13 +0,0 @@
export enum IssueCredential {
proposalSent = 'proposal-sent',
proposalReceived = 'proposal-received',
offerSent = 'offer-sent',
offerReceived = 'offer-received',
declined = 'decliend',
requestSent = 'request-sent',
requestReceived = 'request-received',
credentialIssued = 'credential-issued',
credentialReceived = 'credential-received',
done = 'done',
abandoned = 'abandoned'
}
30 changes: 29 additions & 1 deletion apps/api-gateway/src/issuance/issuance.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
Logger,
BadRequestException,
NotFoundException,
ParseUUIDPipe
ParseUUIDPipe,
Delete
} from '@nestjs/common';
import {
ApiTags,
Expand Down Expand Up @@ -742,4 +743,31 @@ issueCredentialDto.type = 'Issuance';
}
return res.status(HttpStatus.CREATED).json(finalResponse);
}

@Delete('/:orgId/issuance-records')
@ApiOperation({ summary: 'Delete issuance record', description: 'Delete issuance records by orgId' })
@ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto })
@ApiBearerAuth()
@Roles(OrgRoles.OWNER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async deleteIssuanceRecordsByOrgId(
@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.issueCredentialService.deleteIssuanceRecords(orgId, user);
const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: ResponseMessages.issuance.success.deleteIssuanceRecords
};
return res.status(HttpStatus.OK).json(finalResponse);
}
}
9 changes: 7 additions & 2 deletions apps/api-gateway/src/issuance/issuance.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ 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, IIssuedCredential } from '@credebl/common/interfaces/issuance.interface';
import { ICredentialOfferResponse, IDeletedIssuanceRecords, 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 {

Expand Down Expand Up @@ -158,4 +158,9 @@ export class IssuanceService extends BaseService {
}
}

async deleteIssuanceRecords(orgId: string, userDetails: user): Promise<IDeletedIssuanceRecords> {
const payload = { orgId, userDetails };
return this.sendNatsMessage(this.issuanceProxy, 'delete-issuance-records', payload);
}

}
9 changes: 8 additions & 1 deletion apps/issuance/src/issuance.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import { IClientDetails, IIssuance, IIssueCredentials, IIssueCredentialsDefinitions, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOffer, PreviewRequest, TemplateDetailsInterface } from '../interfaces/issuance.interfaces';
import { IssuanceService } from './issuance.service';
import { ICredentialOfferResponse, IIssuedCredential } from '@credebl/common/interfaces/issuance.interface';
import { ICredentialOfferResponse, IDeletedIssuanceRecords, IIssuedCredential } from '@credebl/common/interfaces/issuance.interface';
import { OOBIssueCredentialDto } from 'apps/api-gateway/src/issuance/dtos/issuance.dto';
import { user } from '@prisma/client';

@Controller()
export class IssuanceController {
Expand Down Expand Up @@ -97,4 +98,10 @@ export class IssuanceController {
async retryeBulkCredentials(payload: { fileId: string, orgId: string, clientId: string }): Promise<string> {
return this.issuanceService.retryBulkCredential(payload.fileId, payload.orgId, payload.clientId);
}

@MessagePattern({ cmd: 'delete-issuance-records' })
async deleteIssuanceRecords(payload: {orgId: string, userDetails: user}): Promise<IDeletedIssuanceRecords> {
const { orgId, userDetails } = payload;
return this.issuanceService.deleteIssuanceRecords(orgId, userDetails);
}
}
3 changes: 2 additions & 1 deletion apps/issuance/src/issuance.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { CacheModule } from '@nestjs/cache-manager';
import * as redisStore from 'cache-manager-redis-store';
import { BulkIssuanceProcessor } from './issuance.processor';
import { AwsService } from '@credebl/aws';
import { UserActivityRepository } from 'libs/user-activity/repositories';

@Module({
imports: [
Expand All @@ -38,6 +39,6 @@ import { AwsService } from '@credebl/aws';
})
],
controllers: [IssuanceController],
providers: [IssuanceService, IssuanceRepository, PrismaService, Logger, OutOfBandIssuance, EmailDto, BulkIssuanceProcessor, AwsService]
providers: [IssuanceService, IssuanceRepository, UserActivityRepository, PrismaService, Logger, OutOfBandIssuance, EmailDto, BulkIssuanceProcessor, AwsService]
})
export class IssuanceModule { }
45 changes: 44 additions & 1 deletion apps/issuance/src/issuance.repository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable camelcase */
import { Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common';
import { ConflictException, Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common';
import { PrismaService } from '@credebl/prisma-service';
// eslint-disable-next-line camelcase
import {
Expand All @@ -25,6 +25,7 @@ import { FileUploadStatus } from 'apps/api-gateway/src/enum';
import { IUserRequest } from '@credebl/user-request/user-request.interface';
import { IIssuedCredentialSearchParams } from 'apps/api-gateway/src/issuance/interfaces';
import { SortValue } from '@credebl/enum/enum';
import { IDeletedIssuanceRecords } from '@credebl/common/interfaces/issuance.interface';
@Injectable()
export class IssuanceRepository {
constructor(
Expand Down Expand Up @@ -587,4 +588,46 @@ export class IssuanceRepository {
throw error;
}
}

async deleteIssuanceRecordsByOrgId(orgId: string): Promise<IDeletedIssuanceRecords> {
try {
const tablesToCheck = ['presentations'];

const referenceCounts = await Promise.all(
tablesToCheck.map((table) => this.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(', ')}`);
}

return await this.prisma.$transaction(async (prisma) => {

const recordsToDelete = await this.prisma.credentials.findMany({
where: { orgId },
select: {
createDateTime: true,
createdBy: true,
connectionId: true,
schemaId: true,
state: true,
orgId: true
}
});

const deleteResult = await prisma.credentials.deleteMany({
where: { orgId }
});

return { deleteResult, recordsToDelete};
});
} catch (error) {
this.logger.error(`Error in deleting issuance records: ${error.message}`);
throw error;
}
}
}
52 changes: 48 additions & 4 deletions apps/issuance/src/issuance.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ResponseMessages } from '@credebl/common/response-messages';
import { ClientProxy, RpcException } from '@nestjs/microservices';
import { map } from 'rxjs';
import { CredentialOffer, FileUpload, FileUploadData, IAttributes, IClientDetails, ICreateOfferResponse, ICredentialPayload, IIssuance, IIssueData, IPattern, IQueuePayload, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails, SendEmailCredentialOffer, TemplateDetailsInterface } from '../interfaces/issuance.interfaces';
import { OrgAgentType, PromiseResult, SchemaType, TemplateIdentifier } from '@credebl/enum/enum';
import { IssuanceProcessState, OrgAgentType, PromiseResult, SchemaType, TemplateIdentifier } from '@credebl/enum/enum';
import * as QRCode from 'qrcode';
import { OutOfBandIssuance } from '../templates/out-of-band-issuance.template';
import { EmailDto } from '@credebl/common/dtos/email.dto';
Expand All @@ -27,11 +27,12 @@ import { FileUploadStatus, FileUploadType } from 'apps/api-gateway/src/enum';
import { AwsService } from '@credebl/aws';
import { io } from 'socket.io-client';
import { IIssuedCredentialSearchParams, IssueCredentialType } from 'apps/api-gateway/src/issuance/interfaces';
import { ICredentialOfferResponse, IIssuedCredential, IJsonldCredential } from '@credebl/common/interfaces/issuance.interface';
import { ICredentialOfferResponse, IDeletedIssuanceRecords, IIssuedCredential, IJsonldCredential } from '@credebl/common/interfaces/issuance.interface';
import { OOBIssueCredentialDto } from 'apps/api-gateway/src/issuance/dtos/issuance.dto';
import { agent_invitations, organisation } from '@prisma/client';
import { RecordType, agent_invitations, organisation, user } from '@prisma/client';
import { createOobJsonldIssuancePayload, validateEmail } from '@credebl/common/cast.helper';
import { sendEmail } from '@credebl/common/send-grid-helper-file';
import { UserActivityRepository } from 'libs/user-activity/repositories';


@Injectable()
Expand All @@ -43,6 +44,7 @@ export class IssuanceService {
@Inject('NATS_CLIENT') private readonly issuanceServiceProxy: ClientProxy,
private readonly commonService: CommonService,
private readonly issuanceRepository: IssuanceRepository,
private readonly userActivityRepository: UserActivityRepository,
@Inject(CACHE_MANAGER) private cacheManager: Cache,
private readonly outOfBandIssuance: OutOfBandIssuance,
private readonly emailData: EmailDto,
Expand Down Expand Up @@ -1465,5 +1467,47 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO
}
}

}
async deleteIssuanceRecords(orgId: string, userDetails: user): Promise<IDeletedIssuanceRecords> {
try {
const deletedCredentialsRecords = await this.issuanceRepository.deleteIssuanceRecordsByOrgId(orgId);

if (0 === deletedCredentialsRecords?.deleteResult?.count) {
throw new NotFoundException(ResponseMessages.issuance.error.issuanceRecordsNotFound);
}

const statusCounts = {
[IssuanceProcessState.REQUEST_SENT]: 0,
[IssuanceProcessState.REQUEST_RECEIVED]: 0,
[IssuanceProcessState.PROPOSAL_SENT]: 0,
[IssuanceProcessState.PROPOSAL_RECEIVED]: 0,
[IssuanceProcessState.OFFER_SENT]: 0,
[IssuanceProcessState.OFFER_RECEIVED]: 0,
[IssuanceProcessState.DONE]: 0,
[IssuanceProcessState.DECLIEND]: 0,
[IssuanceProcessState.CREDENTIAL_RECEIVED]: 0,
[IssuanceProcessState.CREDENTIAL_ISSUED]: 0,
[IssuanceProcessState.ABANDONED]: 0
};

await Promise.all(deletedCredentialsRecords?.recordsToDelete?.map(async (record) => {
statusCounts[record.state]++;
}));

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

const deletedIssuanceData = {
deletedCredentialsRecordsCount : deletedCredentialsRecords?.deleteResult?.count,
deletedRecordsStatusCount: filteredStatusCounts
};

await this.userActivityRepository._orgDeletedActivity(orgId, userDetails, deletedIssuanceData, RecordType.ISSUANCE_RECORD);

return deletedCredentialsRecords;
} catch (error) {
this.logger.error(`[deleteIssuanceRecords] - error in deleting issuance records: ${JSON.stringify(error)}`);
throw new RpcException(error.response ? error.response : error);
}
}
}
10 changes: 9 additions & 1 deletion libs/common/src/interfaces/issuance.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,12 @@ export interface IIssuedCredential {
autoAcceptCredential?: string;
contextCorrelationId?: string;
}


interface IDeletedIssuanceRecordsCount {
count: number;
}

export interface IDeletedIssuanceRecords {
deleteResult: IDeletedIssuanceRecordsCount;
recordsToDelete: IIssuedCredentialResponse[];
}
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 @@ -282,7 +282,8 @@ export const ResponseMessages = {
previewCSV: 'File details fetched successfully',
bulkIssuance: 'Issuance process started. It will take some time',
notFound: 'Schema records not found',
bulkProcess: 'Process initiated for bulk issuance'
bulkProcess: 'Process initiated for bulk issuance',
deleteIssuanceRecords: 'Issuance records deleted'
},
error: {
exists: 'Credentials is already exist',
Expand Down Expand Up @@ -314,7 +315,8 @@ export const ResponseMessages = {
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'
cachedfileData: 'Cached file data does not exist',
issuanceRecordsNotFound: 'Issuance records not found'
}
},
verification: {
Expand Down
14 changes: 14 additions & 0 deletions libs/enum/src/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,20 @@ export enum PromiseResult {
FULFILLED = 'fulfilled'
}

export enum IssuanceProcessState {
PROPOSAL_SENT = 'proposal-sent',
PROPOSAL_RECEIVED = 'proposal-received',
OFFER_SENT = 'offer-sent',
OFFER_RECEIVED = 'offer-received',
DECLIEND = 'decliend',
REQUEST_SENT = 'request-sent',
REQUEST_RECEIVED = 'request-received',
CREDENTIAL_ISSUED = 'credential-issued',
CREDENTIAL_RECEIVED = 'credential-received',
DONE = 'done',
ABANDONED = 'abandoned'
}

export enum VerificationProcessState {
PROPOSAL_SENT = 'proposal-sent',
PROPOSAL_RECEIVED = 'proposal-received',
Expand Down