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: selective bulk issuance #509

Merged
merged 3 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 11 additions & 1 deletion apps/api-gateway/src/issuance/dtos/issuance.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,16 @@ export class ClientDetails {
@Type(() => String)
clientId = '';

userId?: string;
@ApiProperty({ required: false, example: 'issue-data.csv' })
@IsOptional()
@Type(() => String)
fileName = '';

@ApiProperty({ required: false })
@IsOptional()
@Type(() => Boolean)
isSelectiveIssuance?: boolean = false;

userId?: string;

}
57 changes: 48 additions & 9 deletions apps/api-gateway/src/issuance/issuance.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export class IssuanceController {
@Res() res: Response
): Promise<object> {
try {

if (file) {
const fileKey: string = uuidv4();
try {
Expand All @@ -232,7 +233,7 @@ export class IssuanceController {
const reqPayload: RequestPayload = {
credDefId: credentialDefinitionId,
fileKey,
fileName: fileDetails['fileName'].split('.csv')[0]
fileName: fileDetails['fileName'] || file?.filename || file?.originalname
};

const importCsvDetails = await this.issueCredentialService.importCsv(reqPayload);
Expand Down Expand Up @@ -326,21 +327,59 @@ export class IssuanceController {
summary: 'bulk issue credential',
description: 'bulk issue credential'
})
@ApiConsumes('multipart/form-data')
@ApiBody({
schema: {
type: 'object',
nullable: false,
required: ['file'],
properties: {
file: {
type: 'string',
format: 'binary'
}
}
},
required: true
})
@UseInterceptors(FileInterceptor('file'))

async issueBulkCredentials(
@Param('requestId') requestId: string,
@Param('orgId') orgId: string,
@Res() res: Response,
@Body() clientDetails: ClientDetails,
@User() user: user
@User() user: user,
@Query('credDefId') credentialDefinitionId?: string,
@Body() fileDetails?: object,
@UploadedFile() file?: Express.Multer.File
): Promise<Response> {

clientDetails.userId = user.id;
const bulkIssuanceDetails = await this.issueCredentialService.issueBulkCredential(requestId, orgId, clientDetails);
const finalResponse: IResponseType = {
statusCode: HttpStatus.CREATED,
message: ResponseMessages.issuance.success.bulkIssuance,
data: bulkIssuanceDetails.response
};
return res.status(HttpStatus.CREATED).json(finalResponse);
let reqPayload: RequestPayload;

if (file && clientDetails?.isSelectiveIssuance) {
const fileKey: string = uuidv4();
try {
await this.awsService.uploadCsvFile(fileKey, file.buffer);
} catch (error) {
throw new RpcException(error.response ? error.response : error);
}

reqPayload = {
credDefId: credentialDefinitionId,
fileKey,
fileName: fileDetails['fileName'] || file?.filename || file?.originalname
};
}
const bulkIssuanceDetails = await this.issueCredentialService.issueBulkCredential(requestId, orgId, clientDetails, reqPayload);

const finalResponse: IResponse = {
statusCode: HttpStatus.CREATED,
message: ResponseMessages.issuance.success.bulkIssuance,
data: bulkIssuanceDetails
};
return res.status(HttpStatus.CREATED).json(finalResponse);
}

@Get('/orgs/:orgId/bulk/files')
Expand Down
6 changes: 3 additions & 3 deletions apps/api-gateway/src/issuance/issuance.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ export class IssuanceService extends BaseService {
return this.sendNats(this.issuanceProxy, 'issued-file-data', payload);
}

async issueBulkCredential(requestId: string, orgId: string, clientDetails: ClientDetails): Promise<{ response: object }> {
const payload = { requestId, orgId, clientDetails };
return this.sendNats(this.issuanceProxy, 'issue-bulk-credentials', payload);
async issueBulkCredential(requestId: string, orgId: string, clientDetails: ClientDetails, reqPayload: RequestPayload): Promise<object> {
const payload = { requestId, orgId, clientDetails, reqPayload };
return this.sendNatsMessage(this.issuanceProxy, 'issue-bulk-credentials', payload);
}

async retryBulkCredential(fileId: string, orgId: string, clientId: string): Promise<{ response: object }> {
Expand Down
5 changes: 3 additions & 2 deletions apps/issuance/interfaces/issuance.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,11 @@ export interface FileUploadData {
jobId: string;
}

export interface ClientDetails {
export interface IClientDetails {
clientId: string;

userId?: string;
isSelectiveIssuance?: boolean;
fileName?: string;
}
export interface IIssuedCredentialsSearchInterface {
issuedCredentialsSearchCriteria: IIssuedCredentialsSearchCriteria;
Expand Down
6 changes: 3 additions & 3 deletions apps/issuance/src/issuance.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import { ClientDetails, ICreateOfferResponse, IIssuance, IIssueCredentials, IIssueCredentialsDefinitions, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOffer, PreviewRequest } from '../interfaces/issuance.interfaces';
import { IClientDetails, ICreateOfferResponse, IIssuance, IIssueCredentials, IIssueCredentialsDefinitions, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOffer, PreviewRequest } from '../interfaces/issuance.interfaces';
import { IssuanceService } from './issuance.service';
import { IIssuedCredential } from '@credebl/common/interfaces/issuance.interface';
import { OOBIssueCredentialDto } from 'apps/api-gateway/src/issuance/dtos/issuance.dto';
Expand Down Expand Up @@ -82,8 +82,8 @@ export class IssuanceController {


@MessagePattern({ cmd: 'issue-bulk-credentials' })
async issueBulkCredentials(payload: { requestId: string, orgId: string, clientDetails: ClientDetails }): Promise<string> {
return this.issuanceService.issueBulkCredential(payload.requestId, payload.orgId, payload.clientDetails);
async issueBulkCredentials(payload: { requestId: string, orgId: string, clientDetails: IClientDetails, reqPayload: ImportFileDetails }): Promise<string> {
return this.issuanceService.issueBulkCredential(payload.requestId, payload.orgId, payload.clientDetails, payload.reqPayload);
}

@MessagePattern({ cmd: 'retry-bulk-credentials' })
Expand Down
26 changes: 16 additions & 10 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 { ClientDetails, FileUploadData, ICredentialAttributesInterface, ImportFileDetails, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces';
import { ClientDetails, FileUploadData, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces';
import { FileUploadData, IClientDetails, ICreateOfferResponse, IIssuance, IIssueData, IPattern, ISendOfferNatsPayload, ImportFileDetails, IssueCredentialWebhookPayload, OutOfBandCredentialOfferPayload, PreviewRequest, SchemaDetails } from '../interfaces/issuance.interfaces';
import { OrgAgentType } from '@credebl/enum/enum';
// import { platform_config } from '@prisma/client';
import * as QRCode from 'qrcode';
Expand Down Expand Up @@ -397,6 +397,8 @@ export class IssuanceService {
label: outOfBandCredential.label || undefined
};

this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this log if not necessary for payload.


const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(outOfBandIssuancePayload, url, apiKey);

if (!credentialCreateOfferDetails) {
Expand Down Expand Up @@ -436,8 +438,9 @@ export class IssuanceService {
disposition: 'attachment'
}
];

const isEmailSent = await sendEmail(this.emailData);
this.logger.log(`isEmailSent ::: ${JSON.stringify(isEmailSent)}`);

if (!isEmailSent) {
errors.push(new InternalServerErrorException(ResponseMessages.issuance.error.emailSend));
Expand Down Expand Up @@ -636,7 +639,7 @@ export class IssuanceService {
}


async importAndPreviewDataForIssuance(importFileDetails: ImportFileDetails): Promise<string> {
async importAndPreviewDataForIssuance(importFileDetails: ImportFileDetails, requestId?: string): Promise<string> {
try {

const credDefResponse =
Expand Down Expand Up @@ -672,9 +675,7 @@ export class IssuanceService {

// Output invalid emails
if (0 < invalidEmails.length) {

throw new BadRequestException(`Invalid emails found in the chosen file`);

}

const fileData: string[] = parsedData.data.map(Object.values);
Expand Down Expand Up @@ -703,7 +704,7 @@ export class IssuanceService {

const newCacheKey = uuidv4();

await this.cacheManager.set(newCacheKey, JSON.stringify(resData), 3600);
await this.cacheManager.set(requestId ? requestId : newCacheKey, JSON.stringify(resData), 60000);

return newCacheKey;

Expand Down Expand Up @@ -806,7 +807,7 @@ export class IssuanceService {
return new Promise(resolve => setTimeout(resolve, ms));
}

async issueBulkCredential(requestId: string, orgId: string, clientDetails: ClientDetails): Promise<string> {
async issueBulkCredential(requestId: string, orgId: string, clientDetails: IClientDetails, reqPayload: ImportFileDetails): Promise<string> {
const fileUpload: {
lastChangedDateTime: Date;
name?: string;
Expand All @@ -829,11 +830,17 @@ export class IssuanceService {
}

try {
const cachedData = await this.cacheManager.get(requestId);
let cachedData = await this.cacheManager.get(requestId);
if (!cachedData) {
throw new BadRequestException(ResponseMessages.issuance.error.cacheTimeOut);
}

if (cachedData && clientDetails?.isSelectiveIssuance) {
await this.cacheManager.del(requestId);
await this.importAndPreviewDataForIssuance(reqPayload, requestId);
// await this.cacheManager.set(requestId, reqPayload);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this commented code in next PR.

cachedData = await this.cacheManager.get(requestId);
}
const parsedData = JSON.parse(cachedData as string).fileData.data;
const parsedPrimeDetails = JSON.parse(cachedData as string);

Expand Down Expand Up @@ -1025,8 +1032,8 @@ export class IssuanceService {
0 === errorCount ? FileUploadStatus.completed : FileUploadStatus.partially_completed;

if (!jobDetails.isRetry) {
this.cacheManager.del(jobDetails.cacheId);
socket.emit('bulk-issuance-process-completed', {clientId: jobDetails.clientId, fileUploadId: jobDetails.fileUploadId});
this.cacheManager.del(jobDetails.cacheId);
} else {
socket.emit('bulk-issuance-process-retry-completed', { clientId: jobDetails.clientId });
}
Expand All @@ -1035,7 +1042,6 @@ export class IssuanceService {
status,
lastChangedDateTime: new Date()
});

}
} catch (error) {
this.logger.error(`Error in completing bulk issuance process: ${error}`);
Expand Down