• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DataBiosphere / consent / #6242

29 Jul 2025 07:41PM UTC coverage: 83.19% (+0.09%) from 83.102%
#6242

push

web-flow
[DT-1982] RADAR email triggers (#2622)

Co-authored-by: rushtong <grushton@broadinstitute.org>
Co-authored-by: Gregory Rushton <rushtong@users.noreply.github.com>
Co-authored-by: Rachel Johanek <55256690+rjohanek@users.noreply.github.com>
Co-authored-by: rjohanek <rjohanek@broadinstitute.org>
Co-authored-by: Matt Bemis <MatthewBemis@users.noreply.github.com>

291 of 328 new or added lines in 12 files covered. (88.72%)

1 existing line in 1 file now uncovered.

10848 of 13040 relevant lines covered (83.19%)

0.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.14
/src/main/java/org/broadinstitute/consent/http/service/EmailService.java
1
package org.broadinstitute.consent.http.service;
2

3
import com.google.common.annotations.VisibleForTesting;
4
import com.google.inject.Inject;
5
import com.sendgrid.Response;
6
import com.sendgrid.helpers.mail.Mail;
7
import com.sendgrid.helpers.mail.objects.Content;
8
import com.sendgrid.helpers.mail.objects.Email;
9
import freemarker.template.Template;
10
import freemarker.template.TemplateException;
11
import java.io.IOException;
12
import java.io.StringWriter;
13
import java.io.Writer;
14
import java.time.Instant;
15
import java.util.Date;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.Objects;
19
import javax.annotation.Nullable;
20
import org.broadinstitute.consent.http.configurations.ConsentConfiguration;
21
import org.broadinstitute.consent.http.db.MailMessageDAO;
22
import org.broadinstitute.consent.http.db.UserDAO;
23
import org.broadinstitute.consent.http.enumeration.EmailType;
24
import org.broadinstitute.consent.http.mail.SendGridAPI;
25
import org.broadinstitute.consent.http.mail.freemarker.FreeMarkerTemplateHelper;
26
import org.broadinstitute.consent.http.mail.message.DACMembersDARRADARApprovedMessage;
27
import org.broadinstitute.consent.http.mail.message.DaaRequestMessage;
28
import org.broadinstitute.consent.http.mail.message.DarExpirationReminderMessage;
29
import org.broadinstitute.consent.http.mail.message.DarExpiredMessage;
30
import org.broadinstitute.consent.http.mail.message.DataCustodianApprovalMessage;
31
import org.broadinstitute.consent.http.mail.message.DatasetApprovedMessage;
32
import org.broadinstitute.consent.http.mail.message.DatasetDeniedMessage;
33
import org.broadinstitute.consent.http.mail.message.DatasetSubmittedMessage;
34
import org.broadinstitute.consent.http.mail.message.MailMessage;
35
import org.broadinstitute.consent.http.mail.message.NewCaseMessage;
36
import org.broadinstitute.consent.http.mail.message.NewDAAUploadResearcherMessage;
37
import org.broadinstitute.consent.http.mail.message.NewDAAUploadSOMessage;
38
import org.broadinstitute.consent.http.mail.message.NewDARRequestMessage;
39
import org.broadinstitute.consent.http.mail.message.NewLibraryCardIssuedMessage;
40
import org.broadinstitute.consent.http.mail.message.NewProgressReportCaseMessage;
41
import org.broadinstitute.consent.http.mail.message.NewProgressReportRequestMessage;
42
import org.broadinstitute.consent.http.mail.message.NewResearcherLibraryRequestMessage;
43
import org.broadinstitute.consent.http.mail.message.ReminderMessage;
44
import org.broadinstitute.consent.http.mail.message.ResearcherApprovedProgressReportMessage;
45
import org.broadinstitute.consent.http.mail.message.ResearcherCloseoutCompletedMessage;
46
import org.broadinstitute.consent.http.mail.message.ResearcherDarApprovedMessage;
47
import org.broadinstitute.consent.http.mail.message.SoDARApproved;
48
import org.broadinstitute.consent.http.mail.message.SoDARSubmitted;
49
import org.broadinstitute.consent.http.mail.message.SoPRApproved;
50
import org.broadinstitute.consent.http.mail.message.SoPRSubmitted;
51
import org.broadinstitute.consent.http.mail.message.SubmittedCloseoutMessage;
52
import org.broadinstitute.consent.http.models.Dataset;
53
import org.broadinstitute.consent.http.models.User;
54
import org.broadinstitute.consent.http.models.Vote;
55
import org.broadinstitute.consent.http.models.dto.DatasetMailDTO;
56
import org.broadinstitute.consent.http.util.ConsentLogger;
57

58
public class EmailService implements ConsentLogger {
59

60
  private final UserDAO userDAO;
61
  private final MailMessageDAO emailDAO;
62
  private final FreeMarkerTemplateHelper templateHelper;
63
  private final SendGridAPI sendGridAPI;
64
  private final String fromAccount;
65
  private final String serverUrl;
66

67
  @Inject
68
  public EmailService(
69
      UserDAO userDAO,
70
      MailMessageDAO emailDAO,
71
      SendGridAPI sendGridAPI,
72
      FreeMarkerTemplateHelper helper,
73
      ConsentConfiguration config) {
1✔
74
    this.userDAO = userDAO;
1✔
75
    this.templateHelper = helper;
1✔
76
    this.emailDAO = emailDAO;
1✔
77
    this.sendGridAPI = sendGridAPI;
1✔
78
    this.serverUrl = config.getServicesConfiguration().getLocalURL();
1✔
79
    this.fromAccount = config.getMailConfiguration().getGoogleAccount();
1✔
80
  }
1✔
81

82
  /**
83
   * This method saves an email (either sent or unsent) with all available metadata from the
84
   * SendGrid response.
85
   */
86
  private void saveEmailAndResponse(
87
      @Nullable Response response,
88
      @Nullable String entityReferenceId,
89
      @Nullable Integer voteId,
90
      Integer userId,
91
      EmailType emailType,
92
      String content) {
93
    Instant now = Instant.now();
1✔
94
    Instant dateSent = (Objects.nonNull(response) && response.getStatusCode() < 400) ? now : null;
1✔
95
    emailDAO.insert(
1✔
96
        entityReferenceId,
97
        voteId,
98
        userId,
99
        emailType.getTypeInt(),
1✔
100
        dateSent,
101
        content,
102
        Objects.nonNull(response) ? response.getBody() : null,
1✔
103
        Objects.nonNull(response) ? response.getStatusCode() : null,
1✔
104
        now);
105
  }
1✔
106

107
  @VisibleForTesting
108
  protected void sendMessage(MailMessage mailMessage, Integer userId)
109
      throws IOException, TemplateException {
110
    Writer out = new StringWriter();
1✔
111
    Template template = templateHelper.getTemplate(mailMessage.getTemplateName());
1✔
112
    template.process(mailMessage.createModel(serverUrl), out);
1✔
113
    String content = out.toString();
1✔
114
    Mail message = new Mail(new Email(fromAccount), mailMessage.createSubject(),
1✔
115
        new Email(mailMessage.toUser.getEmail()), new Content("text/html", content));
1✔
116
    Response response = sendGridAPI.sendMessage(message, mailMessage.toUser.getEmail());
1✔
117
    saveEmailAndResponse(
1✔
118
        response,
119
        mailMessage.getEntityReferenceId(),
1✔
120
        mailMessage.getVoteId(),
1✔
121
        userId,
122
        mailMessage.emailType,
123
        content);
124
  }
1✔
125

126
  public List<org.broadinstitute.consent.http.models.mail.MailMessage> fetchEmailMessagesByType(
127
      EmailType emailType, Integer limit,
128
      Integer offset) {
129
    return emailDAO.fetchMessagesByType(emailType.getTypeInt(), limit, offset);
1✔
130
  }
131

132
  public List<org.broadinstitute.consent.http.models.mail.MailMessage> fetchEmailMessagesByCreateDate(
133
      Date start, Date end, Integer limit,
134
      Integer offset) {
135
    return emailDAO.fetchMessagesByCreateDate(start, end, limit, offset);
1✔
136
  }
137

138

139
  public void sendResearcherDarApproved(
140
      String darCode,
141
      Integer researcherId,
142
      List<DatasetMailDTO> datasets,
143
      String dataUseRestriction,
144
      boolean radarApproved)
145
      throws TemplateException, IOException {
146
    User user = userDAO.findUserById(researcherId);
×
147
    sendMessage(
×
148
        new ResearcherDarApprovedMessage(user, darCode, datasets, dataUseRestriction, radarApproved), researcherId);
149
  }
×
150

151
  public void sendResearcherProgressReportApproved(
152
      String darCode,
153
      Integer researcherId,
154
      List<DatasetMailDTO> datasets,
155
      String dataUseRestriction)
156
      throws TemplateException, IOException {
157
    User user = userDAO.findUserById(researcherId);
×
158
    sendMessage(
×
159
        new ResearcherApprovedProgressReportMessage(user, darCode, datasets, dataUseRestriction), researcherId);
160
  }
×
161

162
  public void sendDataCustodianApprovalMessage(
163
      User custodian,
164
      String darCode,
165
      List<DatasetMailDTO> datasets,
166
      String dataDepositorName,
167
      String researcherEmail,
168
      boolean radarApproved)
169
      throws TemplateException, IOException {
170
    sendMessage(
×
171
        new DataCustodianApprovalMessage(
172
            custodian, darCode, datasets, dataDepositorName, researcherEmail, radarApproved),
173
        custodian.getUserId());
×
174
  }
×
175

176
  public void sendDatasetSubmittedMessage(
177
      User dacChair, User dataSubmitter, String dacName, String datasetName)
178
      throws TemplateException, IOException {
179
    sendMessage(
1✔
180
        new DatasetSubmittedMessage(dacChair, dataSubmitter.getDisplayName(), datasetName, dacName),
1✔
181
        dacChair.getUserId());
1✔
182
  }
1✔
183

184
  public void sendDatasetApprovedMessage(User user, String dacName, String datasetName)
185
      throws TemplateException, IOException {
186
    sendMessage(new DatasetApprovedMessage(user, dacName, datasetName), user.getUserId());
×
187
  }
×
188

189
  public void sendDatasetDeniedMessage(
190
      User user, String dacName, String datasetName, String dacEmail)
191
      throws TemplateException, IOException {
192
    sendMessage(new DatasetDeniedMessage(user, dacName, datasetName, dacEmail), user.getUserId());
×
193
  }
×
194

195
  public void sendNewResearcherMessage(User researcher, User signingOfficial)
196
      throws TemplateException, IOException {
197
    sendMessage(
1✔
198
        new NewResearcherLibraryRequestMessage(signingOfficial, researcher),
199
        researcher.getUserId());
1✔
200
  }
1✔
201

202
  public void sendDaaRequestMessage(
203
      User signingOfficial, User requestUser, String daaName, Integer daaId)
204
      throws TemplateException, IOException {
205
    sendMessage(
1✔
206
        new DaaRequestMessage(signingOfficial, requestUser, daaName, daaId),
207
        requestUser.getUserId());
1✔
208
  }
1✔
209

210
  public void sendNewDAAUploadSOMessage(
211
      User signingOfficial,
212
      String dacName,
213
      String previousDaaName,
214
      String newDaaName,
215
      Integer userId)
216
      throws TemplateException, IOException {
217
    sendMessage(
1✔
218
        new NewDAAUploadSOMessage(signingOfficial, dacName, previousDaaName, newDaaName), userId);
219
  }
1✔
220

221
  public void sendNewDAAUploadResearcherMessage(
222
      User researcher, String dacName, String previousDaaName, String newDaaName, Integer userId)
223
      throws TemplateException, IOException {
224
    sendMessage(
1✔
225
        new NewDAAUploadResearcherMessage(
226
            researcher, dacName, previousDaaName, newDaaName),
227
        userId);
228
  }
1✔
229

230
  public void sendDarNewCollectionElectionMessage(List<User> users, String darCode)
231
      throws IOException, TemplateException {
232
    String electionType = "Data Access Request";
×
233
    for (User user : users) {
×
234
      sendMessage(new NewCaseMessage(user, darCode, electionType), user.getUserId());
×
235
    }
×
236
  }
×
237

238
  public void sendProgressReportNewCollectionElectionMessage(List<User> users, String darCode)
239
      throws IOException, TemplateException {
240
    for (User user : users) {
×
241
      sendMessage(new NewProgressReportCaseMessage(user, darCode), user.getUserId());
×
242
    }
×
243
  }
×
244

245
  public void sendNewDARRequestEmail(
246
      User user, Map<String, List<String>> sendList, String researcherName, String darCode)
247
      throws TemplateException, IOException {
248
        sendMessage(new NewDARRequestMessage(user, darCode, sendList, researcherName),
×
249
        user.getUserId());
×
250
  }
×
251

252
  public void sendNewProgressReportRequestEmail(
253
      User user, Map<String, List<String>> sendList, String researcherName, String darCode, String referenceId)
254
      throws TemplateException, IOException {
255
      sendMessage(new NewProgressReportRequestMessage(user, darCode, referenceId, sendList, researcherName),
×
256
        user.getUserId());
×
257
  }
×
258

259
  /**
260
   * Send a message to a Signing Official that a new Data Access Request has been submitted.
261
   *
262
   * @param user The user to send the message to
263
   * @param darCode The Data Access Request code which is submitted
264
   * @param researcher The researcher whose DAR has been submitted
265
   * @param referenceId The reference ID of the DAR
266
   * @param datasets The datasets associated with the DAR
267
   * @throws TemplateException Template processing exception
268
   * @throws IOException IOException when processing the template or sending the email
269
   */
270
  public void sendNewSoDARSubmittedEmail(User user, String darCode, User researcher, String referenceId, List<Dataset> datasets)
271
      throws TemplateException, IOException {
272
        sendMessage(new SoDARSubmitted(user, darCode, researcher, referenceId, datasets),
×
273
        user.getUserId());
×
274
  }
×
275

276
  /**
277
   * Send a message to a Signing Official that a new progress report has been submitted.
278
   *
279
   * @param user The user to send the message to
280
   * @param darCode The Data Access Request code for which the progress report is submitted
281
   * @param researcher The researcher whose progress report has been submitted
282
   * @param referenceId The reference ID of the progress report
283
   * @param datasets The datasets associated with the progress report
284
   * @throws TemplateException Template processing exception
285
   * @throws IOException IOException when processing the template or sending the email
286
   */
287
  public void sendNewSoProgressReportSubmittedEmail(User user, String darCode, User researcher, String referenceId, List<Dataset> datasets)
288
      throws TemplateException, IOException {
289
      sendMessage(new SoPRSubmitted(user, darCode, researcher, referenceId, datasets),
×
290
        user.getUserId());
×
291
  }
×
292

293
  /**
294
   * Send a message to a Signing Official that a new Data Access Request has been approved.
295
   *
296
   * @param user The user to send the message to
297
   * @param darCode The Data Access Request code which is approved
298
   * @param researcher The researcher whose DAR has been approved
299
   * @param referenceId The reference ID of the DAR
300
   * @param datasets The datasets associated with the DAR
301
   * @param dataUseRestriction The data use restriction associated with the datasets in the DAR
302
   * @throws TemplateException Template processing exception
303
   * @throws IOException IOException when processing the template or sending the email
304
   */
305
  public void sendNewSoDARApprovedEmail(User user, String darCode, User researcher, String referenceId, List<Dataset> datasets, String dataUseRestriction, boolean radarApproved)
306
      throws TemplateException, IOException {
NEW
307
        sendMessage(new SoDARApproved(user, darCode, researcher, referenceId, datasets, dataUseRestriction, radarApproved),
×
308
        user.getUserId());
×
309
  }
×
310

311
  /**
312
   * Send a message to a Signing Official that a new progress report has been approved.
313
   *
314
   * @param user The user to send the message to
315
   * @param darCode The Data Access Request code for which the progress report is approved
316
   * @param researcher The researcher whose progress report has been approved
317
   * @param referenceId The reference ID of the progress report
318
   * @param datasets The datasets associated with the progress report
319
   * @param dataUseRestriction The data use restriction associated with the datasets in the progress report
320
   * @throws TemplateException Template processing exception
321
   * @throws IOException IOException when processing the template or sending the email
322
   */
323
  public void sendNewSoProgressReportApprovedEmail(User user, String darCode, User researcher, String referenceId, List<Dataset> datasets, String dataUseRestriction)
324
      throws TemplateException, IOException {
325
      sendMessage(new SoPRApproved(user, darCode, researcher, referenceId, datasets, dataUseRestriction),
×
326
        user.getUserId());
×
327
  }
×
328

329
  /**
330
   * Send a message to a researcher that their data access request has expired.
331
   *
332
   * @param researcher  the researcher to send the message to
333
   * @param darCode     the data access request code that's expired
334
   * @param userId      the user id of the person sending the message
335
   * @param referenceId the data access request reference id that's expired
336
   */
337
  public void sendDarExpiredMessage(User researcher, String darCode, Integer userId,
338
      String referenceId)
339
      throws TemplateException, IOException {
340
    sendMessage(new DarExpiredMessage(researcher, darCode, referenceId), userId);
1✔
341
  }
1✔
342

343
  /**
344
   * Remind the user that their data access request is about to expire.
345
   *
346
   * @param user        the user to send the message to
347
   * @param darCode     the data access request code that's about to expire
348
   * @param userId      the user id of the person sending the message
349
   * @param referenceId the data access request reference id that is expiring
350
   */
351
  public void sendDarExpirationReminderMessage(User user, String darCode, Integer userId,
352
      String referenceId)
353
      throws TemplateException, IOException {
354
    sendMessage(new DarExpirationReminderMessage(user, darCode, referenceId), userId);
1✔
355
  }
1✔
356

357
  public void sendReminderMessage(User user, Vote vote, String darCode, String electionType, String url)
358
      throws TemplateException, IOException {
359
    sendMessage(new ReminderMessage(user, vote, darCode, electionType, url), user.getUserId());
×
360
  }
×
361

362
  /**
363
   * Send a message to a user that their closeout has been completed.
364
   *
365
   * @param user        the user to send the message to
366
   * @param darCode     the data access request code for which closeout is completed
367
   * @param referenceId the data access request reference id for which closeout is completed
368
   */
369
  public void sendResearcherCloseoutCompletedMessage(User user, String darCode, String referenceId)
370
      throws TemplateException, IOException {
371
    sendMessage(new ResearcherCloseoutCompletedMessage(user, darCode, referenceId),
1✔
372
        user.getUserId());
1✔
373
  }
1✔
374

375
  /**
376
   * Send a message to a Signing Official or a DAC member that a closeout has been submitted for
377
   * review.
378
   *
379
   * @param toUser      The user to send the message to
380
   * @param darId       The Data Access Request ID associated with the closeout
381
   * @param referenceId The Reference ID of the closeout request
382
   * @param closeoutUrl The URL to the closeout request for review
383
   * @throws TemplateException Template processing exception
384
   * @throws IOException       IOException when processing the template or sending the email
385
   */
386
  public void sendSubmittedCloseoutMessage(User toUser, String darId, String referenceId, String closeoutUrl)
387
      throws TemplateException, IOException {
388
    sendMessage(new SubmittedCloseoutMessage(toUser, darId, referenceId, closeoutUrl), toUser.getUserId());
1✔
389
  }
1✔
390

391
  /**
392
   * Send a message to the user when they are issued a library card
393
   *
394
   * @param toUser The user to send the message to
395
   * @throws TemplateException Template processing exception
396
   * @throws IOException IOException when processing the template or sending the email
397
   */
398
  public void sendNewLibraryCardIssuedMessage(User toUser) throws TemplateException, IOException {
399
    sendMessage(new NewLibraryCardIssuedMessage(toUser), toUser.getUserId());
1✔
400
  }
1✔
401

402
  public void sendNewDARRADARApprovalToDAC(
403
      User dacMember,
404
      String darCode,
405
      String referenceId,
406
      List<DatasetMailDTO> datasetList,
407
      User researcher)
408
      throws TemplateException, IOException {
409
    sendMessage(
1✔
410
        new DACMembersDARRADARApprovedMessage(
411
            dacMember, darCode, researcher, referenceId, datasetList),
412
        dacMember.getUserId());
1✔
413
  }
1✔
414
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc