Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/credebl/credebl-platform
Browse files Browse the repository at this point in the history
…into out-of-band
  • Loading branch information
KulkarniShashank committed Oct 26, 2023
2 parents 9bc7771 + 6d0ea0a commit f6a38bc
Show file tree
Hide file tree
Showing 17 changed files with 1,068 additions and 259 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,25 @@ export class CredentialDefinitionController {
};
return res.status(HttpStatus.CREATED).json(credDefResponse);
}

@Get('/orgs/:orgId/bulk/cred-defs')
@ApiOperation({
summary: 'Fetch all credential definition for bulk opeartion',
description: 'Fetch all credential definition from metadata saved in database for bulk opeartion.'
})
@ApiResponse({ status: 200, description: 'Success', type: ApiResponseDto })
@Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER)
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
async getAllCredDefAndSchemaForBulkOperation(
@Param('orgId') orgId: number,
@Res() res: Response
): Promise<object> {
const credentialsDefinitionDetails = await this.credentialDefinitionService.getAllCredDefAndSchemaForBulkOperation(orgId);
const credDefResponse: IResponseType = {
statusCode: HttpStatus.OK,
message: ResponseMessages.credentialDefinition.success.fetch,
data: credentialsDefinitionDetails.response
};
return res.status(HttpStatus.CREATED).json(credDefResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ export class CredentialDefinitionService extends BaseService {
const payload = { schemaId };
return this.sendNats(this.credDefServiceProxy, 'get-all-credential-definitions-by-schema-id', payload);
}

getAllCredDefAndSchemaForBulkOperation(orgId:number): Promise<{ response: object }> {
const payload = { orgId };
return this.sendNats(this.credDefServiceProxy, 'get-all-schema-cred-defs-for-bulk-operation', payload);
}
}
36 changes: 36 additions & 0 deletions apps/api-gateway/src/helper-files/file-operation.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { promisify } from "util";
import * as fs from "fs";


export const createFile = async (
path: string,
fileName: string,
data: string
): Promise<void> => {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
if (!checkIfFileOrDirectoryExists(path)) {

fs.mkdirSync(path);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const writeFile = promisify(fs.writeFile);
return fs.writeFileSync(`${path}/${fileName}`, data, 'utf8');
};

export const checkIfFileOrDirectoryExists = (path: string): boolean => fs.existsSync(path);

export const getFile = async (
path: string,
encoding: BufferEncoding
): Promise<string | Buffer> => {
const readFile = promisify(fs.readFile);

return encoding ? readFile(path, {encoding}) : readFile(path, {});
};


export const deleteFile = async (path: string): Promise<void> => {
const unlink = promisify(fs.unlink);

return unlink(path);
};
6 changes: 6 additions & 0 deletions apps/api-gateway/src/issuance/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,10 @@ export class IUserRole {
export class IUserOrg {
id: number;
orgName: string;
}

export interface FileExportResponse {
response: unknown;
fileContent: string;
fileName : string
}
27 changes: 26 additions & 1 deletion apps/api-gateway/src/issuance/issuance.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
Query,
Get,
Param,
UseFilters
UseFilters,
Header
} from '@nestjs/common';
import {
ApiTags,
Expand Down Expand Up @@ -43,6 +44,7 @@ import { OrgRoles } from 'libs/org-roles/enums';
import { OrgRolesGuard } from '../authz/guards/org-roles.guard';
import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler';
import { ImageServiceService } from '@credebl/image-service';
import { FileExportResponse } from './interfaces';

@Controller()
@UseFilters(CustomExceptionFilter)
Expand Down Expand Up @@ -258,4 +260,27 @@ export class IssuanceController {
return res.status(HttpStatus.CREATED).json(finalResponse);

}

@Get('/orgs/:orgId/download')
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
@Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER)
@ApiUnauthorizedResponse({ status: 401, description: 'Unauthorized', type: UnauthorizedErrorDto })
@ApiForbiddenResponse({ status: 403, description: 'Forbidden', type: ForbiddenErrorDto })
@Header('Content-Disposition', 'attachment; filename="schema.csv"')
@Header('Content-Type', 'application/csv')
@ApiOperation({
summary: 'Download csv template for bulk-issuance',
description: 'Download csv template for bulk-issuance'
})
async downloadSchemaCsvFile(
@Query('credDefid') credentialDefinitionId: string,
@Res() res: Response
): Promise<object> {
try {
const exportedData: FileExportResponse = await this.issueCredentialService.exportSchemaToCSV(credentialDefinitionId);
return res.header('Content-Disposition', `attachment; filename="${exportedData.fileName}.csv"`).status(200).send(exportedData.fileContent);
} catch (error) {
}

}
}
8 changes: 8 additions & 0 deletions apps/api-gateway/src/issuance/issuance.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ClientProxy } from '@nestjs/microservices';
import { BaseService } from 'libs/service/base.service';
import { IUserRequest } from '@credebl/user-request/user-request.interface';
import { IssuanceDto, IssueCredentialDto, OutOfBandCredentialDto } from './dtos/issuance.dto';
import { FileExportResponse } from './interfaces';

@Injectable()
export class IssuanceService extends BaseService {
Expand Down Expand Up @@ -56,4 +57,11 @@ export class IssuanceService extends BaseService {
const payload = { user, outOfBandCredentialDto };
return this.sendNats(this.issuanceProxy, 'out-of-band-credential-offer', payload);
}

async exportSchemaToCSV(credentialDefinitionId: string
): Promise<FileExportResponse> {
const payload = { credentialDefinitionId };
return (await this.sendNats(this.issuanceProxy, 'export-schema-to-csv-by-credDefId', payload)).response;
}

}
8 changes: 7 additions & 1 deletion apps/issuance/interfaces/issuance.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,10 @@ export interface OutOfBandCredentialOfferPayload {
export interface OutOfBandCredentialOffer {
user: IUserRequest;
outOfBandCredentialDto: OutOfBandCredentialOfferPayload;
}
}
export interface SchemaDetails {
credentialDefinitionId: string;
tag: string;
schemaLedgerId: string;
attributes: string;
}
11 changes: 11 additions & 0 deletions apps/issuance/src/issuance.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Controller, Logger } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import { IIssuance, IIssuanceWebhookInterface, IIssueCredentials, IIssueCredentialsDefinitions, OutOfBandCredentialOffer } from '../interfaces/issuance.interfaces';
import { IssuanceService } from './issuance.service';
import { of } from 'rxjs';

@Controller()
export class IssuanceController {
Expand Down Expand Up @@ -43,4 +44,14 @@ export class IssuanceController {
const { outOfBandCredentialDto } = payload;
return this.issuanceService.outOfBandCredentialOffer(outOfBandCredentialDto);
}

@MessagePattern({ cmd: 'export-schema-to-csv-by-credDefId' })
async exportSchemaToCSV(payload: {
credentialDefinitionId: string
}): Promise<object> {

const response = await this.issuanceService.exportSchemaToCSV(payload.credentialDefinitionId);

return of(response).pipe();
}
}
38 changes: 38 additions & 0 deletions apps/issuance/src/issuance.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PrismaService } from '@credebl/prisma-service';
// eslint-disable-next-line camelcase
import { agent_invitations, credentials, org_agents, organisation, platform_config, shortening_url } from '@prisma/client';
import { ResponseMessages } from '@credebl/common/response-messages';
import { SchemaDetails } from '../interfaces/issuance.interfaces';
@Injectable()
export class IssuanceRepository {

Expand Down Expand Up @@ -159,4 +160,41 @@ export class IssuanceRepository {
throw new InternalServerErrorException(error);
}
}

async getCredentialDefinitionDetails(credentialDefinitionId: string): Promise<SchemaDetails> {
try {
const credentialDefinitionDetails = await this.prisma.credential_definition.findFirst({
where: {
credentialDefinitionId
}
});

if (!credentialDefinitionDetails) {
throw new NotFoundException(`Credential definition not found for ID: ${credentialDefinitionId}`);
}

const schemaDetails = await this.prisma.schema.findFirst({
where: {
schemaLedgerId: credentialDefinitionDetails.schemaLedgerId
}
});

if (!schemaDetails) {
throw new NotFoundException(`Schema not found for credential definition ID: ${credentialDefinitionId}`);
}

const credentialDefRes = {
credentialDefinitionId: credentialDefinitionDetails.credentialDefinitionId,
tag: credentialDefinitionDetails.tag,
schemaLedgerId: schemaDetails.schemaLedgerId,
attributes: schemaDetails.attributes
};

return credentialDefRes;
} catch (error) {
this.logger.error(`Error in getCredentialDefinitionDetails: ${error.message}`);
throw new InternalServerErrorException(error.message);
}
}

}
55 changes: 54 additions & 1 deletion apps/issuance/src/issuance.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import { CommonConstants } from '@credebl/common/common.constant';
import { ResponseMessages } from '@credebl/common/response-messages';
import { ClientProxy, RpcException } from '@nestjs/microservices';
import { map } from 'rxjs';
import { ICredentialAttributesInterface, OutOfBandCredentialOfferPayload } from '../interfaces/issuance.interfaces';
import { ICredentialAttributesInterface, OutOfBandCredentialOfferPayload, SchemaDetails } from '../interfaces/issuance.interfaces';
import { OrgAgentType } from '@credebl/enum/enum';
import { platform_config } from '@prisma/client';
import * as QRCode from 'qrcode';
import { OutOfBandIssuance } from '../templates/out-of-band-issuance.template';
import { EmailDto } from '@credebl/common/dtos/email.dto';
import { sendEmail } from '@credebl/common/send-grid-helper-file';
import { join } from 'path';
import { parse } from 'json2csv';
import { checkIfFileOrDirectoryExists, createFile } from '../../api-gateway/src/helper-files/file-operation.helper';


@Injectable()
Expand Down Expand Up @@ -446,4 +449,54 @@ export class IssuanceService {
throw error;
}
}

async exportSchemaToCSV(credentialDefinitionId: string): Promise<object> {
try {
const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails(credentialDefinitionId);

const jsonData = [];
const attributesArray = JSON.parse(schemaResponse.attributes);

// Extract the 'attributeName' values from the objects and store them in an array
const attributeNameArray = attributesArray.map(attribute => attribute.attributeName);
attributeNameArray.unshift('email');

const [csvData, csvFields] = [jsonData, attributeNameArray];

if (!csvData || !csvFields) {
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('Unable to transform schema data for CSV.');
}

const csv = parse(csvFields, { fields: csvFields });

const filePath = join(process.cwd(), `uploadedFiles/exports`);


let processedFileName: string = credentialDefinitionId;
processedFileName = processedFileName.replace(/[\/:*?"<>|]/g, '_');
const timestamp = Math.floor(Date.now() / 1000);
const fileName = `${processedFileName}-${timestamp}.csv`;

await createFile(filePath, fileName, csv);
this.logger.log(`File created - ${fileName}`);
const fullFilePath = join(process.cwd(), `uploadedFiles/exports/${fileName}`);

if (!checkIfFileOrDirectoryExists(fullFilePath)) {
throw new NotFoundException(ResponseMessages.bulkIssuance.error.PathNotFound);
}

// https required to download csv from frontend side
const filePathToDownload = `${process.env.API_GATEWAY_PROTOCOL_SECURE}://${process.env.UPLOAD_LOGO_HOST}/${fileName}`;

return {
fileContent: filePathToDownload,
fileName: processedFileName
};
} catch (error) {
throw new Error('An error occurred during CSV export.');
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { MessagePattern } from '@nestjs/microservices';
import { GetAllCredDefsPayload, GetCredDefBySchemaId } from './interfaces/create-credential-definition.interface';
import { CreateCredDefPayload, GetCredDefPayload } from './interfaces/create-credential-definition.interface';
import { credential_definition } from '@prisma/client';
import { CredDefSchema } from './interfaces/credential-definition.interface';

@Controller('credential-definitions')
export class CredentialDefinitionController {
Expand Down Expand Up @@ -50,4 +51,9 @@ export class CredentialDefinitionController {
async getCredentialDefinitionBySchemaId(payload: GetCredDefBySchemaId): Promise<credential_definition[]> {
return this.credDefService.getCredentialDefinitionBySchemaId(payload);
}

@MessagePattern({ cmd: 'get-all-schema-cred-defs-for-bulk-operation' })
async getAllCredDefAndSchemaForBulkOperation (payload: {orgId : number}): Promise<CredDefSchema[]> {
return this.credDefService.getAllCredDefAndSchemaForBulkOperation(payload.orgId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { CredentialDefinitionRepository } from './repositories/credential-defini
import { CreateCredDefPayload, CredDefPayload, GetAllCredDefsPayload, GetCredDefBySchemaId, GetCredDefPayload } from './interfaces/create-credential-definition.interface';
import { credential_definition } from '@prisma/client';
import { ResponseMessages } from '@credebl/common/response-messages';
import { CreateCredDefAgentRedirection, GetCredDefAgentRedirection } from './interfaces/credential-definition.interface';
import { CreateCredDefAgentRedirection, CredDefSchema, GetCredDefAgentRedirection } from './interfaces/credential-definition.interface';
import { map } from 'rxjs/operators';

@Injectable()
Expand Down Expand Up @@ -266,4 +266,29 @@ export class CredentialDefinitionService extends BaseService {
throw new RpcException(error.response ? error.response : error);
}
}

async getAllCredDefAndSchemaForBulkOperation(orgId: number): Promise<CredDefSchema[]> {
try {
const payload = {
orgId,
sortValue: 'ASC',
credDefSortBy: 'id'
};

const credDefSchemaList: CredDefSchema[] =
await this.credentialDefinitionRepository.getAllCredDefsByOrgIdForBulk(
payload
);
if (!credDefSchemaList) {
throw new NotFoundException(ResponseMessages.credentialDefinition.error.NotFound);
}
return credDefSchemaList;
} catch (error) {
this.logger.error(
`get Cred-Defs and schema List By OrgId for bulk operations: ${JSON.stringify(error)}`
);
throw new RpcException(error.response);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,14 @@ export interface GetCredDefAgentRedirection {
export interface GetCredDefFromTenantPayload {
credentialDefinitionId: string;
}

export interface CredDefSchema {
credentialDefinitionId: string;
schemaCredDefName: string;
}

export interface BulkCredDefSchema {
orgId: number
sortValue: string,
credDefSortBy: string
}
Loading

0 comments on commit f6a38bc

Please sign in to comment.