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: added dynamic support to issuance email template. #849

Merged
merged 5 commits into from
Jul 16, 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
20 changes: 19 additions & 1 deletion apps/api-gateway/src/issuance/dtos/issuance.dto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/array-type */

import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { ArrayMaxSize, ArrayMinSize, IsArray, IsBoolean, IsDefined, IsEmail, IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, MaxLength, ValidateNested } from 'class-validator';
import { ArrayMaxSize, ArrayMinSize, IsArray, IsBoolean, IsDefined, IsEmail, IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, IsUrl, MaxLength, ValidateNested } from 'class-validator';
import { IsCredentialJsonLdContext, SingleOrArray } from '../utils/helper';
import { IssueCredentialType, JsonLdCredentialDetailCredentialStatusOptions, JsonLdCredentialDetailOptionsOptions, JsonObject } from '../interfaces';
import { Transform, Type } from 'class-transformer';
Expand Down Expand Up @@ -495,6 +495,24 @@ export class ClientDetails {
isSelectiveIssuance?: boolean = false;

userId?: string;

@ApiPropertyOptional({ example: 'https://example.com/logo.png' })
@Transform(({ value }) => trim(value))
@IsOptional()
@IsUrl({
// eslint-disable-next-line camelcase
require_protocol: true,
// eslint-disable-next-line camelcase
require_tld: true
},
{ message: 'brandLogoUrl should be a valid URL' })
organizationLogoUrl?: string;

@ApiPropertyOptional({ example: 'MyPlatform' })
@Transform(({ value }) => trim(value))
@IsOptional()
@IsString({ message: 'platformName should be string' })
platformName?: string;

}

Expand Down
3 changes: 1 addition & 2 deletions apps/api-gateway/src/issuance/issuance.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ async downloadBulkIssuanceCSVTemplate(
})
async previewFileDataForIssuance(
@Param('orgId') orgId: string,
@Query(new ValidationPipe({ transform: true })) query: RequestIdQuery,
@Param(new ValidationPipe({ transform: true })) query: RequestIdQuery,
@Query() previewFileDetails: PreviewFileDetails,
@Res() res: Response
): Promise<Response> {
Expand Down Expand Up @@ -412,7 +412,6 @@ async downloadBulkIssuanceCSVTemplate(
@Body() fileDetails?: object,
@UploadedFile() file?: Express.Multer.File
): Promise<Response> {

const { credDefId } = query;
clientDetails.userId = user.id;
let reqPayload;
Expand Down
8 changes: 8 additions & 0 deletions apps/issuance/interfaces/issuance.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ export interface IClientDetails {
userId?: string;
isSelectiveIssuance?: boolean;
fileName?: string;
organizationLogoUrl?: string;
platformName?: string;
}
export interface IIssuedCredentialsSearchInterface {
issuedCredentialsSearchCriteria: IIssuedCredentialsSearchCriteria;
Expand Down Expand Up @@ -274,6 +276,8 @@ export interface SendEmailCredentialOffer {
url: string;
orgId: string;
organizationDetails: organisation;
platformName?: string,
organizationLogoUrl?: string;
}

export interface TemplateDetailsInterface {
Expand Down Expand Up @@ -316,6 +320,8 @@ export interface IQueuePayload{
totalJobs: number;
isRetry: boolean;
isLastData: boolean;
organizationLogoUrl?: string;
platformName?: string;
}

interface FileDetails {
Expand Down Expand Up @@ -352,4 +358,6 @@ export interface BulkPayloadDetails {
orgId: string,
requestId?: string,
isRetry: boolean
organizationLogoUrl?: string,
platformName?: string;
}
35 changes: 24 additions & 11 deletions apps/issuance/src/issuance.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ export class IssuanceService {
}
}

async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayload): Promise<boolean> {
async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayload, platformName?: string, organizationLogoUrl?: string): Promise<boolean> {
try {
const {
credentialOffer,
Expand Down Expand Up @@ -637,6 +637,8 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl
url: string;
orgId: string;
organizationDetails: organisation;
platformName?: string;
organizationLogoUrl?: string;
} = {
credentialType,
protocolVersion,
Expand All @@ -651,7 +653,9 @@ async outOfBandCredentialOffer(outOfBandCredential: OutOfBandCredentialOfferPayl
organizationDetails,
iterator: undefined,
emailId: emailId || '',
index: 0
index: 0,
platformName: platformName || null,
organizationLogoUrl: organizationLogoUrl || null
};

if (credentialOffer) {
Expand Down Expand Up @@ -714,11 +718,14 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO
errors,
url,
orgId,
organizationDetails
organizationDetails,
platformName,
organizationLogoUrl
} = sendEmailCredentialOffer;
const iterationNo = index + 1;
try {


let outOfBandIssuancePayload;
if (IssueCredentialType.INDY === credentialType) {

Expand Down Expand Up @@ -779,7 +786,9 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO
}

const invitationUrl: string = credentialCreateOfferDetails.response?.invitationUrl;

const shortenUrl: string = await this.storeIssuanceObjectReturnUrl(invitationUrl);

const deeplLinkURL = convertUrlToDeepLinkUrl(shortenUrl);

if (!invitationUrl) {
Expand All @@ -795,8 +804,9 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO
}
this.emailData.emailFrom = platformConfigData?.emailFrom;
this.emailData.emailTo = iterator?.emailId ?? emailId;
this.emailData.emailSubject = `${process.env.PLATFORM_NAME} Platform: Issuance of Your Credential`;
this.emailData.emailHtml = this.outOfBandIssuance.outOfBandIssuance(emailId, organizationDetails.name, deeplLinkURL);
const platform = platformName || process.env.PLATFORM_NAME;
this.emailData.emailSubject = `${platform} Platform: Issuance of Your Credential`;
this.emailData.emailHtml = this.outOfBandIssuance.outOfBandIssuance(emailId, organizationDetails.name, deeplLinkURL, platformName, organizationLogoUrl);
this.emailData.emailAttachments = [
{
filename: 'qrcode.png',
Expand All @@ -806,7 +816,8 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO
}
];

const isEmailSent = await sendEmail(this.emailData);

const isEmailSent = await sendEmail(this.emailData);

this.logger.log(`isEmailSent ::: ${JSON.stringify(isEmailSent)}-${this.counter}`);
this.counter++;
Expand Down Expand Up @@ -1225,7 +1236,6 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO
*/

private async processInBatches(bulkPayload, bulkPayloadDetails: BulkPayloadDetails):Promise<void> {

const {clientId, isRetry, orgId, requestId} = bulkPayloadDetails;
const delay = (ms: number): Promise<void> => new Promise<void>((resolve) => setTimeout(resolve, ms));
const batchSize = CommonConstants.ISSUANCE_BATCH_SIZE; // initial 1000
Expand Down Expand Up @@ -1258,7 +1268,9 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO
credentialType: item.credential_type,
totalJobs: bulkPayload.length,
isRetry,
isLastData: false
isLastData: false,
organizationLogoUrl: bulkPayloadDetails?.organizationLogoUrl,
platformName: bulkPayloadDetails?.platformName
}
}));

Expand Down Expand Up @@ -1304,7 +1316,6 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO
if (!requestId) {
throw new BadRequestException(ResponseMessages.issuance.error.missingRequestId);
}

const fileUpload: FileUpload = {
lastChangedDateTime: null,
upload_type: '',
Expand Down Expand Up @@ -1373,7 +1384,9 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO
clientId: clientDetails.clientId,
orgId,
requestId,
isRetry: false
isRetry: false,
organizationLogoUrl: clientDetails?.organizationLogoUrl,
platformName: clientDetails?.platformName
};

this.processInBatches(bulkPayload, bulkPayloadDetails);
Expand Down Expand Up @@ -1492,7 +1505,7 @@ async sendEmailForCredentialOffer(sendEmailCredentialOffer: SendEmailCredentialO


const oobCredentials = await this.outOfBandCredentialOffer(
oobIssuancepayload
oobIssuancepayload, jobDetails?.platformName, jobDetails?.organizationLogoUrl
);

if (oobCredentials) {
Expand Down
17 changes: 14 additions & 3 deletions apps/issuance/templates/out-of-band-issuance.template.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
export class OutOfBandIssuance {
public outOfBandIssuance(email: string, orgName: string, deepLinkURL: string): string {
public outOfBandIssuance(
email: string,
orgName: string,
deepLinkURL: string,
platformName?: string,
organizationLogoUrl?: string
): string {

const logoUrl = organizationLogoUrl || process.env.BRAND_LOGO;
const platform = platformName || process.env.PLATFORM_NAME;
const poweredBy = platformName || process.env.POWERED_BY;

try {
return `<!DOCTYPE html>
<html lang="en">
Expand Down Expand Up @@ -28,7 +39,7 @@ export class OutOfBandIssuance {
<body style="margin: 0px; padding:0px; background-color:#F9F9F9;">
<div style="margin: auto; max-width: 450px; padding: 20px 30px; background-color: #FFFFFF; display:block;">
<div style="display: block; text-align:center;" >
<img src="${process.env.BRAND_LOGO}" alt="${process.env.PLATFORM_NAME} logo" style="max-width:100px; background: white; padding: 5px;border-radius: 5px;" width="100%" height="fit-content" class="CToWUd" data-bit="iit">
<img src="${logoUrl}" alt="${platform} logo" style="max-width:100px; background: white; padding: 5px;border-radius: 5px;" width="100%" height="fit-content" class="CToWUd" data-bit="iit">
</div>
<div style="font-family: Montserrat; font-style: normal; font-weight: 500;
font-size: 15px; line-height: 24px;color: #00000;">
Expand Down Expand Up @@ -77,7 +88,7 @@ export class OutOfBandIssuance {

</div>
<p style="margin-top: 6px;">
© ${process.env.POWERED_BY}
© ${poweredBy}
</p>
</footer>
</div>
Expand Down