diff --git a/apps/api/src/controllers/applicationController.ts b/apps/api/src/controllers/applicationController.ts index 8662b88c2..5acd7b76c 100644 --- a/apps/api/src/controllers/applicationController.ts +++ b/apps/api/src/controllers/applicationController.ts @@ -28,8 +28,9 @@ import type { import { ApplicationStates } from '@pcgl-daco/data-model'; import type { SectionRoutesValues, UpdateEditApplicationRequest } from '@pcgl-daco/validation'; -import { getEmailConfig } from '@/config/emailConfig.ts'; +import { authConfig } from '@/config/authConfig.ts'; import { getDbInstance } from '@/db/index.js'; +import { getGroupEmails } from '@/external/pcglAuthZClient.ts'; import BaseLogger from '@/logger.js'; import { type ApplicationListRequest } from '@/routes/types.js'; import { applicationActionSvc } from '@/service/applicationActionService.ts'; @@ -677,16 +678,25 @@ export const submitRevision = async ({ const { actionId } = submittedRevision.data; if (result.data.state === ApplicationStates.DAC_REVISIONS_REQUESTED) { - const { - email: { dacAddress }, - } = getEmailConfig; - emailService.sendEmailDacForSubmittedRevisions({ - id: application.id, - to: dacAddress, - applicantName: applicant_first_name || 'N/A', - submittedDate: new Date(), - actionId, - }); + const accessToken = request.session.account?.accessToken || ''; + const emails = await getGroupEmails( + accessToken, + `${authConfig.AUTHZ_GROUP_PREFIX_DAC_CHAIR}:${application.dac_id}`, + ); + + if (emails.success) { + emails.data.forEach((email) => { + emailService.sendEmailDacForSubmittedRevisions({ + id: application.id, + to: email, + applicantName: applicant_first_name || 'N/A', + submittedDate: new Date(), + actionId, + }); + }); + } else { + logger.error('Failed to retrieve group emails, email to dac members has not been sent', emails.error); + } } else { emailService.sendEmailRepForSubmittedRevisions({ id: application.id, @@ -1057,18 +1067,25 @@ export const submitApplication = async ({ actionId, }); } else if (result.data.state === ApplicationStates.INSTITUTIONAL_REP_REVIEW) { - const { - email: { dacAddress }, - } = getEmailConfig; - - // Send email to DAC for review - emailService.sendEmailDacForReview({ - id: application.id, - to: dacAddress, - applicantName: applicant_first_name || 'N/A', - submittedDate: new Date(), - actionId, - }); + const accessToken = request.session.account?.accessToken || ''; + const emails = await getGroupEmails( + accessToken, + `${authConfig.AUTHZ_GROUP_PREFIX_DAC_CHAIR}:${application.dac_id}`, + ); + if (emails.success) { + emails.data.forEach((email) => { + // Send email to DAC for review + emailService.sendEmailDacForReview({ + id: application.id, + to: email, + applicantName: applicant_first_name || 'N/A', + submittedDate: new Date(), + actionId, + }); + }); + } else { + logger.error('Failed to retrieve group emails, email to dac members has not been sent', emails.error); + } // send email to applicant that application is submitted to DAC emailService.sendEmailApplicantApplicationSubmitted({ diff --git a/apps/api/src/external/pcglAuthZClient.ts b/apps/api/src/external/pcglAuthZClient.ts index b9bd529a1..142f15127 100644 --- a/apps/api/src/external/pcglAuthZClient.ts +++ b/apps/api/src/external/pcglAuthZClient.ts @@ -24,6 +24,7 @@ import urlJoin from 'url-join'; import { fetchWithRetry } from './fetchWithRetry.ts'; import { addUserToStudyPermissionResponse, + authzGroupResponseValidation, authZUserInfo, ServiceTokenResponse, type PCGLAddUserToStudyPermissionResponse, @@ -219,3 +220,37 @@ export const addUsersToStudyPermission = async ( return failure('SYSTEM_ERROR', message); } }; + +/** + * @param accessToken + * @param group + * @returns returns a list of emails associated with the given group + */ +export const getGroupEmails = async ( + accessToken: string, + group: string, +): AsyncResult => { + try { + const response = await fetchAuthZResource(`/group/${group}`, accessToken); + + if (response.status === 204) { + // A "204 No content" response is returned when the user is not registered. + return failure('NOT_FOUND', 'Unable to retrieve user information from the PCGL AuthZ service.'); + } + + const res = await response.json(); + + const resultGroup = authzGroupResponseValidation.safeParse(res); + + if (!resultGroup.success) { + const message = `AuthZ service returned unexpected when look for groups`; + logger.error(`[AUTHZ]: ${message}`, JSON.stringify(res)); + return failure('SYSTEM_ERROR', message); + } + + return success(resultGroup.data.emails); + } catch (error) { + logger.error(`[AUTHZ]: Unexpected error while getting user info from the AuthZ service.`, error); + return failure('SYSTEM_ERROR', `Error contacting the PCGL Authorization Service.`); + } +}; diff --git a/apps/api/src/external/types.ts b/apps/api/src/external/types.ts index acadaba2d..f6f864629 100644 --- a/apps/api/src/external/types.ts +++ b/apps/api/src/external/types.ts @@ -85,3 +85,9 @@ export const ServiceTokenResponse = z.object({ token: z.string(), }); export type ServiceTokenResponse = z.infer; + +export const authzGroupResponseValidation = z.object({ + user_id: z.array(z.string()), + emails: z.array(z.string()), +}); +export type PCGLAuthZGroupResponse = z.infer;