-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
revised email notification files to use event subscriber model; chang…
…ed design to pull emails for byPass and transitions from the config file
- Loading branch information
Showing
10 changed files
with
386 additions
and
258 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
src/components/progress-report/workflow/events/workflow-updated.event.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { ID, Session, UnsecuredDto } from '~/common'; | ||
import { ProgressReportStatus as Status } from '../../dto'; | ||
import { ProgressReportWorkflowEvent as WorkflowEvent } from '../dto/workflow-event.dto'; | ||
import { InternalTransition } from '../transitions'; | ||
|
||
export class WorkflowUpdatedEvent { | ||
constructor( | ||
readonly reportId: ID, | ||
readonly previousStatus: Status, | ||
readonly next: InternalTransition | Status, | ||
readonly session: Session, | ||
readonly workflowEvent: UnsecuredDto<WorkflowEvent>, | ||
) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './progress-report-workflow-notification.handler'; |
228 changes: 228 additions & 0 deletions
228
...onents/progress-report/workflow/handlers/progress-report-workflow-notification.handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
import { EmailService } from '@seedcompany/nestjs-email'; | ||
import { uniqBy } from 'lodash'; | ||
import { ID, Role, Session, UnsecuredDto } from '~/common'; | ||
import { | ||
ConfigService, | ||
EventsHandler, | ||
IEventHandler, | ||
ILogger, | ||
Logger, | ||
} from '~/core'; | ||
import { | ||
EmailReportStatusNotification, | ||
ProgressReportStatusChanged, | ||
} from '~/core/email/templates/progress-report-status-changed.template'; | ||
import { AuthenticationService } from '../../../authentication'; | ||
import { LanguageService } from '../../../language'; | ||
import { PeriodicReportService } from '../../../periodic-report'; | ||
import { ProjectService } from '../../../project'; | ||
import { UserService } from '../../../user'; | ||
import { ProgressReportStatus as Status } from '../../dto'; | ||
import { ProgressReportWorkflowEvent } from '../dto/workflow-event.dto'; | ||
import { WorkflowUpdatedEvent } from '../events/workflow-updated.event'; | ||
import { ProgressReportWorkflowRepository } from '../progress-report-workflow.repository'; | ||
import { ProgressReportWorkflowService } from '../progress-report-workflow.service'; | ||
import { InternalTransition } from '../transitions'; | ||
|
||
interface Member { | ||
id?: ID | undefined; | ||
email?: string | undefined; | ||
role?: Role[] | undefined; | ||
} | ||
type Recipient = Omit<Member, 'role'>; | ||
|
||
@EventsHandler(WorkflowUpdatedEvent) | ||
export class ProgressReportWorkflowNotificationHandler | ||
implements IEventHandler<WorkflowUpdatedEvent> | ||
{ | ||
constructor( | ||
private readonly auth: AuthenticationService, | ||
private readonly repo: ProgressReportWorkflowRepository, | ||
private readonly configService: ConfigService, | ||
private readonly userService: UserService, | ||
private readonly projectService: ProjectService, | ||
private readonly languageService: LanguageService, | ||
private readonly reportService: PeriodicReportService, | ||
private readonly emailService: EmailService, | ||
private readonly workflowService: ProgressReportWorkflowService, | ||
@Logger('ProgressReportWorkflowNotificationHandler:report-status-changed') | ||
private readonly logger: ILogger, | ||
) {} | ||
|
||
async handle({ | ||
reportId, | ||
previousStatus, | ||
next, | ||
session, | ||
workflowEvent, | ||
}: WorkflowUpdatedEvent) { | ||
if (!this.configService.sendProgressReportNotifications) { | ||
return; | ||
} | ||
|
||
const membersWithValidRoles = await this.getTeamMembersWithValidRoles( | ||
reportId, | ||
next, | ||
); | ||
|
||
const envEmailList = this.getEnvList(next) as []; | ||
const usersInEnvList: Recipient[] = await Promise.all( | ||
envEmailList.map(async (item) => { | ||
const user = await this.repo.getUserInfoByEmail(item); | ||
if (user.length > 0) { | ||
const [{ id, email }] = user; | ||
return { id: id, email: email }; | ||
} else { | ||
return { id: undefined, email: item }; | ||
} | ||
}), | ||
); | ||
|
||
const recipients = uniqBy( | ||
[...usersInEnvList, ...membersWithValidRoles], | ||
'email', | ||
); | ||
const notifications: EmailReportStatusNotification[] = await Promise.all( | ||
recipients.map((recipient) => | ||
this.prepareNotificationObject( | ||
session, | ||
reportId, | ||
previousStatus, | ||
recipient, | ||
workflowEvent, | ||
), | ||
), | ||
); | ||
|
||
this.logger.info('Notifying', { | ||
emails: notifications.map((r) => r.recipient.email.value), | ||
projectId: notifications[0]?.project.id ?? undefined, | ||
languageId: notifications[0]?.language.id ?? undefined, | ||
reportId: reportId, | ||
newStatusVal: notifications[0]?.newStatusVal ?? undefined, | ||
previousStatusVal: notifications[0]?.previousStatusVal ?? undefined, | ||
}); | ||
|
||
for (const notification of notifications) { | ||
if (!notification.recipient.email.value) { | ||
continue; | ||
} | ||
await this.emailService.send( | ||
notification.recipient.email.value, | ||
ProgressReportStatusChanged, | ||
notification, | ||
); | ||
} | ||
} | ||
|
||
private async prepareNotificationObject( | ||
session: Session, | ||
reportId: ID, | ||
previousStatus: Status, | ||
receiver: Recipient, | ||
unsecuredEvent: UnsecuredDto<ProgressReportWorkflowEvent>, | ||
): Promise<EmailReportStatusNotification> { | ||
const { projectId, languageId } = await this.repo.getProjectInfoByReportId( | ||
reportId, | ||
); | ||
|
||
const recipientId = receiver.id | ||
? receiver.id | ||
: this.configService.rootAdmin.id; | ||
const recipientSession = await this.auth.sessionForUser(recipientId); | ||
|
||
const recipient = receiver.id | ||
? await this.userService.readOne(recipientId, recipientSession) | ||
: { | ||
email: { value: receiver.email, canRead: true, canEdit: false }, | ||
displayFirstName: { | ||
value: receiver.email?.split('@')[0], | ||
canRead: true, | ||
canEdit: false, | ||
}, | ||
displayLastName: { value: '', canRead: true, canEdit: false }, | ||
timezone: { | ||
value: this.configService.defaultTimeZone, | ||
canRead: true, | ||
canEdit: false, | ||
}, | ||
}; | ||
|
||
const project = await this.projectService.readOne( | ||
projectId, | ||
recipientSession, | ||
); | ||
const language = await this.languageService.readOne( | ||
languageId, | ||
recipientSession, | ||
); | ||
const report = await this.reportService.readOne(reportId, recipientSession); | ||
const changedBy = await this.userService.readOne( | ||
session.userId, | ||
recipientSession, | ||
); | ||
const workflowEvent = this.workflowService.secure( | ||
unsecuredEvent, | ||
recipientSession, | ||
); | ||
|
||
return { | ||
changedBy, | ||
recipient, | ||
project, | ||
language, | ||
report, | ||
newStatusVal: report.status?.value, | ||
previousStatusVal: previousStatus, | ||
workflowEvent: workflowEvent, | ||
}; | ||
} | ||
|
||
private async getTeamMembersWithValidRoles( | ||
reportId: ID, | ||
next: Status | InternalTransition, | ||
) { | ||
const rolesToAlwaysNotify = [ | ||
Role.ProjectManager, | ||
Role.RegionalDirector, | ||
Role.FieldOperationsDirector, | ||
]; | ||
const allRolesToNotify = | ||
typeof next !== 'string' && next.notify.TeamMembersWithRoles | ||
? [...rolesToAlwaysNotify, ...next.notify.TeamMembersWithRoles] | ||
: [...rolesToAlwaysNotify]; | ||
|
||
const projectMemberInfo = (await this.repo.getProjectMemberInfoByReportId( | ||
reportId, | ||
)) as Member[]; | ||
return projectMemberInfo | ||
.filter((mbr) => | ||
mbr.role?.some((role) => allRolesToNotify.includes(role)), | ||
) | ||
.map((mbr) => { | ||
delete mbr.role; | ||
return mbr; | ||
}); | ||
} | ||
|
||
private getEnvList(next: Status | InternalTransition) { | ||
let envList: string | string[] = []; | ||
typeof next !== 'string' | ||
? this.parseEnv(this.configService.progressReportTransitionEmails) | ||
.filter((item) => item[0] === next.name) | ||
.map((item) => (envList = item[1])) | ||
: this.parseEnv(this.configService.progressReportBypassEmails) | ||
.filter((item) => item[0] === next) | ||
.map((item) => (envList = item[1])); | ||
return envList; | ||
} | ||
|
||
private parseEnv(envString: string) { | ||
return envString | ||
?.split(';') | ||
.map((item) => item.split('=')) | ||
?.map((item) => { | ||
return [item[0], item[1].split(',')]; | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.