Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Context } from 'aws-lambda';
import { EnvironmentVariablesService } from '../lib/environment-variables-service';
import { CompactConfigurationClient } from '../lib/compact-configuration-client';
import { JurisdictionClient } from '../lib/jurisdiction-client';
import { EncumbranceNotificationService, InvestigationNotificationService } from '../lib/email';
import { EmailNotificationService, EncumbranceNotificationService, InvestigationNotificationService } from '../lib/email';
import { EmailNotificationEvent, EmailNotificationResponse } from '../lib/models/email-notification-service-event';

const environmentVariables = new EnvironmentVariablesService();
Expand All @@ -19,6 +19,7 @@ interface LambdaProperties {
}

export class Lambda implements LambdaInterface {
private readonly emailService: EmailNotificationService;
private readonly encumbranceService: EncumbranceNotificationService;
private readonly investigationService: InvestigationNotificationService;

Expand All @@ -40,6 +41,13 @@ export class Lambda implements LambdaInterface {
jurisdictionClient: jurisdictionClient
});

this.emailService = new EmailNotificationService({
logger: logger,
sesClient: props.sesClient,
compactConfigurationClient: compactConfigurationClient,
jurisdictionClient: jurisdictionClient
});

this.investigationService = new InvestigationNotificationService({
logger: logger,
sesClient: props.sesClient,
Expand Down Expand Up @@ -319,6 +327,27 @@ export class Lambda implements LambdaInterface {
event.templateVariables.licenseType
);
break;
case 'homeJurisdictionChangeNotification':
if (!event.jurisdiction) {
throw new Error('Missing required jurisdiction field for home jurisdiction change notification template.');
}
if (!event.templateVariables?.providerFirstName
|| !event.templateVariables?.providerLastName
|| !event.templateVariables?.providerId
|| !event.templateVariables?.previousJurisdiction
|| !event.templateVariables?.newJurisdiction) {
throw new Error('Missing required template variables for home jurisdiction change notification template.');
}
await this.emailService.sendHomeJurisdictionChangeStateNotificationEmail(
event.compact,
event.jurisdiction,
event.templateVariables.providerFirstName,
event.templateVariables.providerLastName,
event.templateVariables.providerId,
event.templateVariables.previousJurisdiction,
event.templateVariables.newJurisdiction
);
break;
default:
logger.info('Unsupported email template provided', { template: event.template });
throw new Error(`Unsupported email template: ${event.template}`);
Expand Down
195 changes: 0 additions & 195 deletions backend/cosmetology-app/lambdas/nodejs/lib/email/base-email-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,201 +367,6 @@ export abstract class BaseEmailService {
report['root']['data']['childrenIds'].push(blockId);
}

protected insertTuple(report: TReaderDocument, keyText: string, valueText: string) {
const containerBlockId = `block-${crypto.randomUUID()}`;
const keyBlockId = `block-${crypto.randomUUID()}`;
const valueBlockId = `block-${crypto.randomUUID()}`;


report[keyBlockId] = {
'type': 'Text',
'data': {
'style': {
'fontWeight': 'bold',
'padding': {
'top': 16,
'bottom': 0,
'right': 12,
'left': 24
}
},
'props': {
'text': keyText
}
}
};

report[valueBlockId] = {
'type': 'Text',
'data': {
'style': {
'color': '#525252',
'fontSize': 14,
'fontWeight': 'normal',
'padding': {
'top': 0,
'bottom': 0,
'right': 24,
'left': 24
}
},
'props': {
'text': valueText
}
}
};

report[containerBlockId] = {
'type': 'Container',
'data': {
'style': {
'padding': {
'top': 0,
'bottom': 0,
'right': 72,
'left': 76
}
},
'props': {
'childrenIds': [
keyBlockId,
valueBlockId
]
}
}
};

report['root']['data']['childrenIds'].push(containerBlockId);
}

protected insertTwoColumnTable(report: TReaderDocument, title: string, rows: { left: string, right: string }[]) {
const titleBlockId = `block-${crypto.randomUUID()}`;


report[titleBlockId] = {
'type': 'Text',
'data': {
'style': {
'fontWeight': 'bold',
'padding': {
'top': 24,
'bottom': 16,
'right': 24,
'left': 68
}
},
'props': {
'text': title
}
}
};

report['root']['data']['childrenIds'].push(titleBlockId);

rows.forEach((row) => {
this.insertTwoColumnRow(report, row.left, row.right, false, 6);
});
}

protected insertTwoColumnRow(
report: TReaderDocument,
leftContent: string,
rightContent: string,
isBold: boolean,
bottomPadding: number
) {
const containerId = `block-${crypto.randomUUID()}`;
const leftCellId = `block-${crypto.randomUUID()}`;
const rightCellId = `block-${crypto.randomUUID()}`;

report[leftCellId] = {
'type': 'Text',
'data': {
'style': {
'fontWeight': 'normal',
'textAlign': 'left',
'padding': {
'top': 0,
'bottom': 0,
'right': 24,
'left': 24
}
},
'props': {
'text': leftContent
}
}
};

report[rightCellId] = {
'type': 'Text',
'data': {
'style': {
'fontWeight': 'normal',
'textAlign': 'right',
'padding': {
'top': 0,
'bottom': 0,
'right': 24,
'left': 24
}
},
'props': {
'text': rightContent
}
}
};

report[containerId] = {
'type': 'ColumnsContainer',
'data': {
'style': {
'padding': {
'top': 0,
'bottom': bottomPadding || 6,
'right': 44,
'left': 44
}
},
'props': {
'fixedWidths': [
null,
null,
null
],
'columnsCount': 2,
'columnsGap': 10,
'columns': [
{
'childrenIds': [
leftCellId
]
},
{
'childrenIds': [
rightCellId
]
},
{
'childrenIds': []
}
]
}
}
};

if (
isBold
&& report[leftCellId]['data']['style']
&& report[rightCellId]['data']['style']
) {
report[leftCellId]['data']['style']['fontWeight'] = 'bold';
report[rightCellId]['data']['style']['fontWeight'] = 'bold';
}

report['root']['data']['childrenIds'].push(containerId);
}

protected insertFooter(report: TReaderDocument) {
const blockId = `block-footer`;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { BaseEmailService } from './base-email-service';
import { EnvironmentVariablesService } from '../environment-variables-service';
import { RecipientType } from '../models/email-notification-service-event';

const environmentVariableService = new EnvironmentVariablesService();

/**
* Email service for handling email notifications
*/
export class EmailNotificationService extends BaseEmailService {

private async getJurisdictionRecipients(
compact: string,
jurisdiction: string,
recipientType: RecipientType
): Promise<string[]> {

const jurisdictionConfig = await this.jurisdictionClient.getJurisdictionConfiguration(compact, jurisdiction);

switch (recipientType) {
case 'JURISDICTION_OPERATIONS_TEAM':
return jurisdictionConfig.jurisdictionOperationsTeamEmails;
default:
throw new Error(`Unsupported recipient type for compact configuration: ${recipientType}`);
}
}

/**
* Sends a notification email to a jurisdiction operations team when a practitioner's home state license changes
* @param compact - The compact name
* @param jurisdiction - The jurisdiction to notify
* @param providerFirstName - The provider's first name
* @param providerLastName - The provider's last name
* @param providerId - The provider's ID
* @param previousJurisdiction - The previous home jurisdiction
* @param newJurisdiction - The new home jurisdiction
*/
public async sendHomeJurisdictionChangeStateNotificationEmail(
compact: string,
jurisdiction: string,
providerFirstName: string,
providerLastName: string,
providerId: string,
previousJurisdiction: string,
newJurisdiction: string
): Promise<void> {
this.logger.info('Sending home jurisdiction change state notification email', {
compact: compact,
jurisdiction: jurisdiction
});

const recipients = await this.getJurisdictionRecipients(
compact,
jurisdiction,
'JURISDICTION_OPERATIONS_TEAM'
);

if (recipients.length === 0) {
throw new Error(`No recipients found for jurisdiction ${jurisdiction} in compact ${compact}`);
}

const formattedPreviousJurisdiction = previousJurisdiction.toUpperCase();
const formattedNewJurisdiction = newJurisdiction.toUpperCase();

const compactConfig = await this.compactConfigurationClient.getCompactConfiguration(compact);
const report = this.getNewEmailTemplate();
const subject = `Practitioner Home State Change - ${compactConfig.compactName}`;
const bodyText = `This is to notify you that ${providerFirstName} ${providerLastName} has changed their home state from ${formattedPreviousJurisdiction} to ${formattedNewJurisdiction}.\n\n` +
`Provider Details: ${environmentVariableService.getUiBasePathUrl()}/${compact}/Licensing/${providerId}\n\n` +
'If the above link does not work, you can copy and paste the url into a browser tab, where you are already logged in.';

this.insertHeader(report, subject);
this.insertBody(report, bodyText, 'center', true);
this.insertFooter(report);

const htmlContent = this.renderTemplate(report);

await this.sendEmail({ htmlContent, subject, recipients, errorMessage: 'Unable to send home jurisdiction change state notification email' });
}
}
1 change: 1 addition & 0 deletions backend/cosmetology-app/lambdas/nodejs/lib/email/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { EncumbranceNotificationService } from './encumbrance-notification-servi
export { InvestigationNotificationService } from './investigation-notification-service';
export { IngestEventEmailService } from './ingest-event-email-service';
export { EnvironmentBannerService } from './environment-banner-service';
export { EmailNotificationService } from './email-notification-service';
Loading
Loading