From 302d6d0c2486efde8180e9b854507f095c7ba584 Mon Sep 17 00:00:00 2001 From: vanessachung Date: Thu, 29 May 2025 16:43:42 -0600 Subject: [PATCH 01/14] add notified flag in Activity.java --- src/main/Activity/Activity.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/Activity/Activity.java b/src/main/Activity/Activity.java index ead1a583..20c0835f 100644 --- a/src/main/Activity/Activity.java +++ b/src/main/Activity/Activity.java @@ -14,6 +14,10 @@ public class Activity implements Comparable { private ObjectId id; + // add notification + @BsonProperty(value = "notified") + private boolean notified = false; + @BsonProperty(value = "occurredAt") private LocalDateTime occurredAt; @@ -74,6 +78,14 @@ public void setId(ObjectId id) { this.id = id; } + public boolean isNotified() { + return notified; + } + + public void setNotified(boolean notified) { + this.notified = notified; + } + // default sort is by occurred at, and then by username private Comparator getComparator() { return Comparator.comparing(Activity::getOccurredAt) From fe93370c1ec8821a945ae5535959755cbcf2a5ae Mon Sep 17 00:00:00 2001 From: vanessachung Date: Thu, 29 May 2025 16:47:10 -0600 Subject: [PATCH 02/14] implement getUnnotifiedActivities in ActivityDaoImpl --- src/main/Database/Activity/ActivityDaoImpl.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/Database/Activity/ActivityDaoImpl.java b/src/main/Database/Activity/ActivityDaoImpl.java index 649adf3e..4a02decf 100644 --- a/src/main/Database/Activity/ActivityDaoImpl.java +++ b/src/main/Database/Activity/ActivityDaoImpl.java @@ -57,6 +57,15 @@ public List getAll() { .sorted(Comparator.reverseOrder()) .collect(Collectors.toList()); } + // + @Override + public List getUnnotifiedActivities() { + return activityCollection.find(eq("notified", false)) + .into(new ArrayList<>()).stream() + .sorted(Comparator.reverseOrder()) // optional + .collect(Collectors.toList()); + } + @Override public int size() { From ac2bfc330c4603205d9501385b9f286d5384efe9 Mon Sep 17 00:00:00 2001 From: vanessachung Date: Sat, 7 Jun 2025 21:29:45 -0600 Subject: [PATCH 03/14] add a new class EmailNotifier --- .../Database/Activity/ActivityDaoImpl.java | 5 +++- src/main/Mail/EmailNotifier.java | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/main/Mail/EmailNotifier.java diff --git a/src/main/Database/Activity/ActivityDaoImpl.java b/src/main/Database/Activity/ActivityDaoImpl.java index 4a02decf..c334f4fb 100644 --- a/src/main/Database/Activity/ActivityDaoImpl.java +++ b/src/main/Database/Activity/ActivityDaoImpl.java @@ -89,6 +89,9 @@ public void update(Activity activity) { @Override public void save(Activity activity) { - activityCollection.insertOne(activity); + + activityCollection.insertOne(activity) + // Trigger email notifications + EmailNotifier.handle(activity);; } } diff --git a/src/main/Mail/EmailNotifier.java b/src/main/Mail/EmailNotifier.java new file mode 100644 index 00000000..5e304c49 --- /dev/null +++ b/src/main/Mail/EmailNotifier.java @@ -0,0 +1,26 @@ +package Mail; + +import Activity.Activity; +import Mail.Services.SendgridService; + +public class EmailNotifier { + + public static void handle(Activity activity) { + switch (activity.getType()) { + case "CreateClientActivity": + SendgridService.sendWelcomeWithQuickStart(activity.getUsername(), activity.getNonprofitState()); + break; + case "UploadFileActivity": + SendgridService.sendUploadReminder(activity.getUsername(), activity.getUploadedDocType()); + break; + case "StartApplicationActivity": + SendgridService.sendApplicationReminder(activity.getUsername()); + break; + case "MailApplicationActivity": + SendgridService.sendPickupInfo(activity.getUsername(), activity.getNonprofit()); + break; + default: + // log or skip + } + } +} From cecf35f9090cdd33773b1a30723b7b4f471238ec Mon Sep 17 00:00:00 2001 From: vanessachung Date: Sat, 7 Jun 2025 21:40:36 -0600 Subject: [PATCH 04/14] set up sendgrid API key --- src/main/Mail/Services/SendgridService.java | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/main/Mail/Services/SendgridService.java diff --git a/src/main/Mail/Services/SendgridService.java b/src/main/Mail/Services/SendgridService.java new file mode 100644 index 00000000..f2f499ce --- /dev/null +++ b/src/main/Mail/Services/SendgridService.java @@ -0,0 +1,51 @@ +package Mail.Services; + +import com.sendgrid.*; +import java.io.IOException; + +public class SendgridService { + private static final String SENDGRID_API_KEY = System.getenv("SENDGRID_API_KEY"); + + private static void sendEmail(String toEmail, String subject, String bodyHtml) { + Email from = new Email("noreply@keep.id"); + Email to = new Email(toEmail); + Content content = new Content("text/html", bodyHtml); + Mail mail = new Mail(from, subject, to, content); + + SendGrid sg = new SendGrid(SENDGRID_API_KEY); + Request request = new Request(); + + try { + request.setMethod(Method.POST); + request.setEndpoint("mail/send"); + request.setBody(mail.build()); + Response response = sg.api(request); + + if (response.getStatusCode() >= 400) { + System.err.println("Email failed: " + response.getBody()); + } + } catch (IOException ex) { + System.err.println("SendGrid Exception: " + ex.getMessage()); + } + } + + public static void sendWelcomeWithQuickStart(String username, String state) { + String subject = "Welcome to Keep.id!"; + String body = "

Hi " + username + ", welcome! Here's how to get your ID in " + state + "...

"; + sendEmail(username + "@example.com", subject, body); // Replace with real email + } + + public static void sendUploadReminder(String username, String docType) { + String subject = "You uploaded a " + docType; + String body = "

We’ve saved your " + docType + ". Here's what to upload next…

"; + sendEmail(username + "@example.com", subject, body); + } + + public static void sendApplicationReminder(String username) { + sendEmail(username + "@example.com", "Finish your application", "

You started an application. Come back and finish it!

"); + } + + public static void sendPickupInfo(String username, String nonprofit) { + sendEmail(username + "@example.com", "Pick up your ID", "

Your documents are ready! Go to " + nonprofit + " to pick them up.

"); + } +} From 8dbef7fecc4fad639c26d91d1a5676085218feb2 Mon Sep 17 00:00:00 2001 From: vanessachung Date: Sat, 7 Jun 2025 22:35:46 -0600 Subject: [PATCH 05/14] successfully test the Sendgrid API key works --- pom.xml | 5 +++++ src/main/Database/Activity/ActivityDaoImpl.java | 5 +++-- src/main/Mail/EmailNotifier.java | 17 ++++++++++++----- src/main/Mail/SendgridTest.java | 12 ++++++++++++ src/main/Mail/Services/SendgridService.java | 12 +++++++++++- 5 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 src/main/Mail/SendgridTest.java diff --git a/pom.xml b/pom.xml index 89f296a2..ef52c516 100644 --- a/pom.xml +++ b/pom.xml @@ -317,5 +317,10 @@ braintree-java 3.11.0 + + com.sendgrid + sendgrid-java + 4.9.3 + \ No newline at end of file diff --git a/src/main/Database/Activity/ActivityDaoImpl.java b/src/main/Database/Activity/ActivityDaoImpl.java index c334f4fb..8d0755d4 100644 --- a/src/main/Database/Activity/ActivityDaoImpl.java +++ b/src/main/Database/Activity/ActivityDaoImpl.java @@ -3,6 +3,7 @@ import Activity.Activity; import Config.DeploymentLevel; import Config.MongoConfig; +import Mail.EmailNotifier; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import org.bson.types.ObjectId; @@ -58,7 +59,7 @@ public List getAll() { .collect(Collectors.toList()); } // - @Override + // @Override public List getUnnotifiedActivities() { return activityCollection.find(eq("notified", false)) .into(new ArrayList<>()).stream() @@ -90,7 +91,7 @@ public void update(Activity activity) { @Override public void save(Activity activity) { - activityCollection.insertOne(activity) + activityCollection.insertOne(activity); // Trigger email notifications EmailNotifier.handle(activity);; } diff --git a/src/main/Mail/EmailNotifier.java b/src/main/Mail/EmailNotifier.java index 5e304c49..b6953eaf 100644 --- a/src/main/Mail/EmailNotifier.java +++ b/src/main/Mail/EmailNotifier.java @@ -3,24 +3,31 @@ import Activity.Activity; import Mail.Services.SendgridService; +import java.util.List; + public class EmailNotifier { public static void handle(Activity activity) { - switch (activity.getType()) { + List types = activity.getType(); + if (types == null || types.isEmpty()) return; + + String type = types.get(types.size() - 1); // get the most specific activity type + + switch (type) { case "CreateClientActivity": - SendgridService.sendWelcomeWithQuickStart(activity.getUsername(), activity.getNonprofitState()); + SendgridService.sendWelcomeWithQuickStart(activity.getUsername(), "PA"); // placeholder break; case "UploadFileActivity": - SendgridService.sendUploadReminder(activity.getUsername(), activity.getUploadedDocType()); + SendgridService.sendUploadReminder(activity.getUsername(), "DocumentType"); // placeholder break; case "StartApplicationActivity": SendgridService.sendApplicationReminder(activity.getUsername()); break; case "MailApplicationActivity": - SendgridService.sendPickupInfo(activity.getUsername(), activity.getNonprofit()); + SendgridService.sendPickupInfo(activity.getUsername(), "NonprofitName"); // placeholder break; default: - // log or skip + // Optional: log unknown type } } } diff --git a/src/main/Mail/SendgridTest.java b/src/main/Mail/SendgridTest.java new file mode 100644 index 00000000..a799c0bc --- /dev/null +++ b/src/main/Mail/SendgridTest.java @@ -0,0 +1,12 @@ +package Mail; + +import Mail.Services.SendgridService; + + +public class SendgridTest { + public static void main(String[] args) { + // System.out.println("SENDGRID_API_KEY = " + System.getenv("SENDGRID_API_KEY")); + SendgridService.sendTestEmail(); + } +} + diff --git a/src/main/Mail/Services/SendgridService.java b/src/main/Mail/Services/SendgridService.java index f2f499ce..741504ce 100644 --- a/src/main/Mail/Services/SendgridService.java +++ b/src/main/Mail/Services/SendgridService.java @@ -1,13 +1,18 @@ package Mail.Services; import com.sendgrid.*; +import com.sendgrid.helpers.mail.objects.Content; +import com.sendgrid.helpers.mail.objects.Email; +import com.sendgrid.helpers.mail.Mail; + + import java.io.IOException; public class SendgridService { private static final String SENDGRID_API_KEY = System.getenv("SENDGRID_API_KEY"); private static void sendEmail(String toEmail, String subject, String bodyHtml) { - Email from = new Email("noreply@keep.id"); + Email from = new Email("vanessachung@keep.id"); Email to = new Email(toEmail); Content content = new Content("text/html", bodyHtml); Mail mail = new Mail(from, subject, to, content); @@ -48,4 +53,9 @@ public static void sendApplicationReminder(String username) { public static void sendPickupInfo(String username, String nonprofit) { sendEmail(username + "@example.com", "Pick up your ID", "

Your documents are ready! Go to " + nonprofit + " to pick them up.

"); } + public static void sendTestEmail() { + String subject = "Test from Keep.id"; + String body = "

Hello! This is a test email sent via SendGrid!

"; + sendEmail("vanessachung@keepid", subject, body); + } } From de5e26577dea7d2c4ee9ba1a4c4cc6836b8f7ef2 Mon Sep 17 00:00:00 2001 From: vanessachung Date: Sat, 7 Jun 2025 22:56:34 -0600 Subject: [PATCH 06/14] integrate Spring scheduler for automated email notifications --- pom.xml | 6 ++++++ src/main/App.java | 3 +++ src/main/Config/AppConfig.java | 4 ++++ src/main/Config/SchedulerConfig.java | 12 ++++++++++++ src/main/Mail/ScheduledEmailDispatcher.java | 21 +++++++++++++++++++++ src/main/Mail/Services/SendgridService.java | 2 +- 6 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/main/Config/SchedulerConfig.java create mode 100644 src/main/Mail/ScheduledEmailDispatcher.java diff --git a/pom.xml b/pom.xml index ef52c516..bebd6c3a 100644 --- a/pom.xml +++ b/pom.xml @@ -322,5 +322,11 @@ sendgrid-java 4.9.3 + + + org.springframework + spring-context + 5.3.33 + \ No newline at end of file diff --git a/src/main/App.java b/src/main/App.java index eca3dba1..1944c903 100644 --- a/src/main/App.java +++ b/src/main/App.java @@ -6,3 +6,6 @@ public static void main(String[] args) { AppConfig.appFactory(DeploymentLevel.STAGING); } } + + + diff --git a/src/main/Config/AppConfig.java b/src/main/Config/AppConfig.java index f72ea142..51be9e2b 100644 --- a/src/main/Config/AppConfig.java +++ b/src/main/Config/AppConfig.java @@ -44,6 +44,8 @@ import java.util.Optional; import lombok.SneakyThrows; import org.bson.types.ObjectId; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + public class AppConfig { public static Long ASYNC_TIME_OUT = 10L; @@ -52,6 +54,8 @@ public class AppConfig { @SneakyThrows public static Javalin appFactory(DeploymentLevel deploymentLevel) { + // Enable Spring's scheduler (cron job support) + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SchedulerConfig.class); System.setProperty("logback.configurationFile", "../Logger/Resources/logback.xml"); Javalin app = AppConfig.createJavalinApp(deploymentLevel); MongoConfig.getMongoClient(); diff --git a/src/main/Config/SchedulerConfig.java b/src/main/Config/SchedulerConfig.java new file mode 100644 index 00000000..7dec4ff1 --- /dev/null +++ b/src/main/Config/SchedulerConfig.java @@ -0,0 +1,12 @@ +package Config; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; + +@Configuration +@EnableScheduling +@ComponentScan(basePackages = {"Mail"}) // ensure your scheduler is scanned +public class SchedulerConfig { +} + diff --git a/src/main/Mail/ScheduledEmailDispatcher.java b/src/main/Mail/ScheduledEmailDispatcher.java new file mode 100644 index 00000000..b3e43be0 --- /dev/null +++ b/src/main/Mail/ScheduledEmailDispatcher.java @@ -0,0 +1,21 @@ +package Mail; + +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class ScheduledEmailDispatcher { + + @Scheduled(cron = "0 0 8 * * *") // Every day at 8 AM + public void dispatchDailyReminders() { + // You can query unfinished activities here and send emails + System.out.println("Running scheduled email job..."); + + // e.g. ActivityDao.getUnfinishedApplications().forEach(EmailNotifier::handle); + } + @Scheduled(fixedRate = 10000) // every 10 seconds + public void testEmailPing() { + System.out.println("Running scheduled task: " + System.currentTimeMillis()); + } +} + diff --git a/src/main/Mail/Services/SendgridService.java b/src/main/Mail/Services/SendgridService.java index 741504ce..9171c338 100644 --- a/src/main/Mail/Services/SendgridService.java +++ b/src/main/Mail/Services/SendgridService.java @@ -56,6 +56,6 @@ public static void sendPickupInfo(String username, String nonprofit) { public static void sendTestEmail() { String subject = "Test from Keep.id"; String body = "

Hello! This is a test email sent via SendGrid!

"; - sendEmail("vanessachung@keepid", subject, body); + sendEmail("vanessachung@keep.id", subject, body); } } From d12b5f58c932142c9786dba02b9bd014710cfc4b Mon Sep 17 00:00:00 2001 From: vanessachung Date: Wed, 18 Jun 2025 21:17:20 +0800 Subject: [PATCH 07/14] use pure Java to implement --- src/main/Activity/Activity.java | 11 ++++- src/main/Config/AppConfig.java | 44 ++++++++++++++++--- src/main/Config/SchedulerConfig.java | 12 ----- src/main/Database/Activity/ActivityDao.java | 7 +++ .../Database/Activity/ActivityDaoImpl.java | 12 +++++ .../Activity/ActivityDaoTestImpl.java | 10 +++++ src/main/Mail/EmailNotifier.java | 2 +- src/main/Mail/ScheduledEmailDispatcher.java | 31 ++++++++----- 8 files changed, 98 insertions(+), 31 deletions(-) delete mode 100644 src/main/Config/SchedulerConfig.java diff --git a/src/main/Activity/Activity.java b/src/main/Activity/Activity.java index 20c0835f..7a6ab0b1 100644 --- a/src/main/Activity/Activity.java +++ b/src/main/Activity/Activity.java @@ -13,8 +13,10 @@ public class Activity implements Comparable { private ObjectId id; + private LocalDateTime notifiedAt; - // add notification + + // add notification @BsonProperty(value = "notified") private boolean notified = false; @@ -68,6 +70,13 @@ public String getUsername() { public ObjectId getId() { return id; } + public LocalDateTime getNotifiedAt() { + return notifiedAt; + } + + public void setNotifiedAt(LocalDateTime notifiedAt) { + this.notifiedAt = notifiedAt; + } public Activity setUsername(String username) { this.username = username; diff --git a/src/main/Config/AppConfig.java b/src/main/Config/AppConfig.java index 51be9e2b..e8b6665b 100644 --- a/src/main/Config/AppConfig.java +++ b/src/main/Config/AppConfig.java @@ -5,6 +5,7 @@ import Billing.BillingController; import Database.Activity.ActivityDao; import Database.Activity.ActivityDaoFactory; +import Database.Activity.ActivityDaoImpl; import Database.File.FileDao; import Database.File.FileDaoFactory; import Database.Form.FormDao; @@ -24,6 +25,7 @@ import Issue.IssueController; import Mail.FileBackfillController; import Mail.MailController; +import Mail.ScheduledEmailDispatcher; import OptionalUserInformation.OptionalUserInformationController; import Organization.Organization; import Organization.OrganizationController; @@ -40,11 +42,20 @@ import com.mongodb.client.MongoDatabase; import io.javalin.Javalin; import io.javalin.http.HttpResponseException; + +import java.time.Duration; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + import lombok.SneakyThrows; import org.bson.types.ObjectId; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; public class AppConfig { @@ -54,11 +65,20 @@ public class AppConfig { @SneakyThrows public static Javalin appFactory(DeploymentLevel deploymentLevel) { - // Enable Spring's scheduler (cron job support) - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SchedulerConfig.class); - System.setProperty("logback.configurationFile", "../Logger/Resources/logback.xml"); + System.setProperty("logback.configurationFile", "../Logger/Resources/logback.xml"); + MongoConfig.getMongoClient(); + ActivityDao activityDao = ActivityDaoFactory.create(deploymentLevel); + ScheduledEmailDispatcher dispatcher = new ScheduledEmailDispatcher(activityDao); + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + executor.scheduleAtFixedRate( + dispatcher::dispatchDailyReminders, + computeInitialDelayTo8AM(), + TimeUnit.DAYS.toSeconds(1), + TimeUnit.SECONDS + ); + // create app Javalin app = AppConfig.createJavalinApp(deploymentLevel); - MongoConfig.getMongoClient(); + // Continue loading other DAOs UserDao userDao = UserDaoFactory.create(deploymentLevel); OptionalUserInformationDao optionalUserInformationDao = OptionalUserInformationDaoFactory.create(deploymentLevel); @@ -66,7 +86,7 @@ public static Javalin appFactory(DeploymentLevel deploymentLevel) { OrgDao orgDao = OrgDaoFactory.create(deploymentLevel); FormDao formDao = FormDaoFactory.create(deploymentLevel); FileDao fileDao = FileDaoFactory.create(deploymentLevel); - ActivityDao activityDao = ActivityDaoFactory.create(deploymentLevel); + //ActivityDao activityDao = ActivityDaoFactory.create(deploymentLevel); MailDao mailDao = MailDaoFactory.create(deploymentLevel); MongoDatabase db = MongoConfig.getDatabase(deploymentLevel); setApplicationHeaders(app); @@ -287,6 +307,18 @@ public static Javalin appFactory(DeploymentLevel deploymentLevel) { app.post("/submit-mail", mailController.saveMail); return app; } + private static long computeInitialDelayTo8AM() { + LocalDateTime now = LocalDateTime.now(); + LocalDateTime nextRun = now.withHour(8).withMinute(0).withSecond(0).withNano(0); + + if (now.isAfter(nextRun)) { + // if now the time is over 8:00 am, schedule to tomorrow + nextRun = nextRun.plusDays(1); + } + + Duration delay = Duration.between(now, nextRun); + return delay.getSeconds(); // return as second + } public static void setApplicationHeaders(Javalin app) { app.before( diff --git a/src/main/Config/SchedulerConfig.java b/src/main/Config/SchedulerConfig.java deleted file mode 100644 index 7dec4ff1..00000000 --- a/src/main/Config/SchedulerConfig.java +++ /dev/null @@ -1,12 +0,0 @@ -package Config; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.EnableScheduling; - -@Configuration -@EnableScheduling -@ComponentScan(basePackages = {"Mail"}) // ensure your scheduler is scanned -public class SchedulerConfig { -} - diff --git a/src/main/Database/Activity/ActivityDao.java b/src/main/Database/Activity/ActivityDao.java index 1ad82bcd..c5ae94f1 100644 --- a/src/main/Database/Activity/ActivityDao.java +++ b/src/main/Database/Activity/ActivityDao.java @@ -12,4 +12,11 @@ public interface ActivityDao extends Dao { List getAllFromUserBetweenInclusive( String username, LocalDateTime startTime, LocalDateTime endTime); + // New: Find up to N unnotified activities (for email reminders) + List findUnnotified(int limit); + + // New: Update an activity (to mark as notified) + void update(Activity activity); + List getUnnotifiedActivities(); } + diff --git a/src/main/Database/Activity/ActivityDaoImpl.java b/src/main/Database/Activity/ActivityDaoImpl.java index 8d0755d4..00f468c0 100644 --- a/src/main/Database/Activity/ActivityDaoImpl.java +++ b/src/main/Database/Activity/ActivityDaoImpl.java @@ -6,7 +6,9 @@ import Mail.EmailNotifier; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.Filters; import org.bson.types.ObjectId; +import org.springframework.stereotype.Repository; import java.time.LocalDateTime; import java.util.ArrayList; @@ -17,6 +19,7 @@ import static com.mongodb.client.model.Filters.eq; +@Repository public class ActivityDaoImpl implements ActivityDao { private final MongoCollection activityCollection; @@ -95,4 +98,13 @@ public void save(Activity activity) { // Trigger email notifications EmailNotifier.handle(activity);; } + @Override + public List findUnnotified(int limit) { + return activityCollection.find(eq("notified", false)) + .limit(limit) + .into(new ArrayList<>()).stream() + .sorted(Comparator.reverseOrder()) + .collect(Collectors.toList()); + } + } diff --git a/src/main/Database/Activity/ActivityDaoTestImpl.java b/src/main/Database/Activity/ActivityDaoTestImpl.java index c2fe31b1..60951fa6 100644 --- a/src/main/Database/Activity/ActivityDaoTestImpl.java +++ b/src/main/Database/Activity/ActivityDaoTestImpl.java @@ -75,4 +75,14 @@ public void update(Activity activity) { public void save(Activity activity) { activityMap.put(activity.getId(), activity); } + + @Override + public List findUnnotified(int limit) { + return new ArrayList<>(); + } + @Override + public List getUnnotifiedActivities() { + return Collections.emptyList(); + } + } diff --git a/src/main/Mail/EmailNotifier.java b/src/main/Mail/EmailNotifier.java index b6953eaf..e7475b5f 100644 --- a/src/main/Mail/EmailNotifier.java +++ b/src/main/Mail/EmailNotifier.java @@ -12,7 +12,7 @@ public static void handle(Activity activity) { if (types == null || types.isEmpty()) return; String type = types.get(types.size() - 1); // get the most specific activity type - + System.out.println("Handling activity type: " + type + " for user: " + activity.getUsername()); switch (type) { case "CreateClientActivity": SendgridService.sendWelcomeWithQuickStart(activity.getUsername(), "PA"); // placeholder diff --git a/src/main/Mail/ScheduledEmailDispatcher.java b/src/main/Mail/ScheduledEmailDispatcher.java index b3e43be0..b861b640 100644 --- a/src/main/Mail/ScheduledEmailDispatcher.java +++ b/src/main/Mail/ScheduledEmailDispatcher.java @@ -1,21 +1,30 @@ package Mail; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; +import Activity.Activity; +import Database.Activity.ActivityDao; + +import java.time.LocalDateTime; +import java.util.List; -@Component public class ScheduledEmailDispatcher { + private final ActivityDao activityDao; + + public ScheduledEmailDispatcher(ActivityDao activityDao) { + this.activityDao = activityDao; + } - @Scheduled(cron = "0 0 8 * * *") // Every day at 8 AM public void dispatchDailyReminders() { - // You can query unfinished activities here and send emails - System.out.println("Running scheduled email job..."); + System.out.println("Running scheduled reminder task: " + LocalDateTime.now()); - // e.g. ActivityDao.getUnfinishedApplications().forEach(EmailNotifier::handle); - } - @Scheduled(fixedRate = 10000) // every 10 seconds - public void testEmailPing() { - System.out.println("Running scheduled task: " + System.currentTimeMillis()); + List unnotified = activityDao.getUnnotifiedActivities(); + for (Activity activity : unnotified) { + // + EmailNotifier.handle(activity); + + activity.setNotified(true); + activity.setNotifiedAt(LocalDateTime.now()); + activityDao.update(activity); + } } } From 2b63ec96136db573205bf941f0c9e054239ba9b5 Mon Sep 17 00:00:00 2001 From: vanessachung Date: Wed, 18 Jun 2025 21:37:22 +0800 Subject: [PATCH 08/14] add EmailNotifierTest and add mockito-inline --- pom.xml | 8 ++- src/test/EmailTest/EmailNotifierTest.java | 68 +++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/test/EmailTest/EmailNotifierTest.java diff --git a/pom.xml b/pom.xml index bebd6c3a..c0afb6d4 100644 --- a/pom.xml +++ b/pom.xml @@ -209,9 +209,15 @@ org.mockito mockito-core - 3.2.0 + 4.11.0 compile + + org.mockito + mockito-inline + 3.2.4 + test + org.junit.jupiter junit-jupiter-api diff --git a/src/test/EmailTest/EmailNotifierTest.java b/src/test/EmailTest/EmailNotifierTest.java new file mode 100644 index 00000000..3cd1f82e --- /dev/null +++ b/src/test/EmailTest/EmailNotifierTest.java @@ -0,0 +1,68 @@ +package EmailTest; + +import Mail.EmailNotifier; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import Activity.Activity; +import Mail.Services.SendgridService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import java.util.Arrays; +import static org.mockito.Mockito.*; + +public class EmailNotifierTest { + + Activity activity; + + @BeforeEach + public void setUp() { + activity = new Activity(); + activity.setUsername("testuser"); + } + + @Test + public void testCreateClientActivity() { + activity.setType(Arrays.asList("Activity", "CreateClientActivity")); + try (MockedStatic mocked = mockStatic(SendgridService.class)) { + EmailNotifier.handle(activity); + mocked.verify(() -> SendgridService.sendWelcomeWithQuickStart("testuser", "PA")); + } + } + + @Test + public void testUploadFileActivity() { + activity.setType(Arrays.asList("Activity", "UploadFileActivity")); + try (MockedStatic mocked = mockStatic(SendgridService.class)) { + EmailNotifier.handle(activity); + mocked.verify(() -> SendgridService.sendUploadReminder("testuser", "DocumentType")); + } + } + + @Test + public void testStartApplicationActivity() { + activity.setType(Arrays.asList("Activity", "StartApplicationActivity")); + try (MockedStatic mocked = mockStatic(SendgridService.class)) { + EmailNotifier.handle(activity); + mocked.verify(() -> SendgridService.sendApplicationReminder("testuser")); + } + } + + @Test + public void testMailApplicationActivity() { + activity.setType(Arrays.asList("Activity", "MailApplicationActivity")); + try (MockedStatic mocked = mockStatic(SendgridService.class)) { + EmailNotifier.handle(activity); + mocked.verify(() -> SendgridService.sendPickupInfo("testuser", "NonprofitName")); + } + } + + @Test + public void testUnknownActivityType() { + activity.setType(Arrays.asList("Activity", "SomethingElse")); + try (MockedStatic mocked = mockStatic(SendgridService.class)) { + EmailNotifier.handle(activity); + mocked.verifyNoInteractions(); + } + } +} From 4ed0cf0935947f5746dc8a8b8ab1feb3b725f243 Mon Sep 17 00:00:00 2001 From: vanessachung Date: Wed, 18 Jun 2025 21:58:08 +0800 Subject: [PATCH 09/14] adjust pom.xml --- pom.xml | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index c0afb6d4..de7a6dfe 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,8 @@ + + net.java.dev.jna @@ -206,16 +208,37 @@ json 20180813 + + + + + + org.mockito mockito-core 4.11.0 - compile + test + org.mockito mockito-inline - 3.2.4 + 4.11.0 + test + + + + net.bytebuddy + byte-buddy + 1.14.11 + test + + + + net.bytebuddy + byte-buddy-agent + 1.14.11 test @@ -242,11 +265,7 @@ 5.7.0 test - - net.bytebuddy - byte-buddy - 1.10.4 - + org.slf4j slf4j-api From 07d84361b3fbe0c657a5ce8919c11bfb79f9d769 Mon Sep 17 00:00:00 2001 From: vanessachung Date: Wed, 18 Jun 2025 22:25:04 +0800 Subject: [PATCH 10/14] test save calls EmailNotifier in ADIUT --- .../Activity/ActivityDaoImplUnitTests.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/test/DatabaseTest/Activity/ActivityDaoImplUnitTests.java b/src/test/DatabaseTest/Activity/ActivityDaoImplUnitTests.java index e6347896..f07b88ea 100644 --- a/src/test/DatabaseTest/Activity/ActivityDaoImplUnitTests.java +++ b/src/test/DatabaseTest/Activity/ActivityDaoImplUnitTests.java @@ -7,12 +7,17 @@ import Database.Activity.ActivityDao; import Database.Activity.ActivityDaoFactory; import File.FileType; +import Mail.EmailNotifier; import TestUtils.EntityFactory; import TestUtils.TestUtils; import java.time.LocalDateTime; +import java.util.Arrays; import java.util.List; import org.bson.types.ObjectId; import org.junit.*; +import static org.mockito.Mockito.*; +import org.mockito.MockedStatic; + public class ActivityDaoImplUnitTests { private ActivityDao activityDao; @@ -247,4 +252,21 @@ static boolean areActivitiesEqual(List activities1, List act } return isEqual; } + +// @Test +// public void testSaveCallsEmailNotifierHandle() { +// Activity activity = new Activity(); +// activity.setUsername("testuser"); +// activity.setType(Arrays.asList("Activity", "UploadFileActivity")); +// +// try (MockedStatic mockedNotifier = mockStatic(EmailNotifier.class)) { +// +// activityDao.save(activity); +// +// +// mockedNotifier.verify(() -> EmailNotifier.handle(activity), times(1)); +// } +// Activity found = activityDao.get(activity.getId()).get(); +// assertEquals("testuser", found.getUsername()); +// } } From 4fa9c1bee9c2f0a3ea5deb934776a9cb019aedd2 Mon Sep 17 00:00:00 2001 From: vanessachung Date: Wed, 18 Jun 2025 23:07:31 +0800 Subject: [PATCH 11/14] sendgrid manual test --- src/main/Mail/EmailNotifier.java | 12 ++++++ src/test/EmailTest/EmailIntTest.java | 35 +++++++++++++++++ src/test/EmailTest/SendgridManualTest.java | 44 ++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 src/test/EmailTest/EmailIntTest.java create mode 100644 src/test/EmailTest/SendgridManualTest.java diff --git a/src/main/Mail/EmailNotifier.java b/src/main/Mail/EmailNotifier.java index e7475b5f..e88415c3 100644 --- a/src/main/Mail/EmailNotifier.java +++ b/src/main/Mail/EmailNotifier.java @@ -26,6 +26,18 @@ public static void handle(Activity activity) { case "MailApplicationActivity": SendgridService.sendPickupInfo(activity.getUsername(), "NonprofitName"); // placeholder break; +// case "SubmitApplicationActivity": +// SendgridService.sendSubmissionConfirmation(user, "ApplicationName"); +// break; +// case "RecoverPasswordActivity": +// SendgridService.sendPasswordRecoveryAlert(user); +// break; +// case "ChangePasswordActivity": +// SendgridService.sendPasswordChangeConfirmation(user); +// break; +// case "Change2FAActivity": +// SendgridService.send2FAUpdateNotice(user, activity.getObjectName()); // "on"/"off" +// break; default: // Optional: log unknown type } diff --git a/src/test/EmailTest/EmailIntTest.java b/src/test/EmailTest/EmailIntTest.java new file mode 100644 index 00000000..3941ef89 --- /dev/null +++ b/src/test/EmailTest/EmailIntTest.java @@ -0,0 +1,35 @@ +package EmailTest; + +import Activity.Activity; +import Mail.EmailNotifier; +import java.util.Arrays; +import org.junit.Test; + +public class EmailIntTest { + + @Test + public void testSendEmailsForAllActivities() { + String testEmail = "vanessa137222@gmail.com"; + + Activity createClient = new Activity(); + createClient.setUsername(testEmail); + createClient.setType(Arrays.asList("Activity", "CreateClientActivity")); + EmailNotifier.handle(createClient); + + Activity uploadFile = new Activity(); + uploadFile.setUsername(testEmail); + uploadFile.setType(Arrays.asList("Activity", "UploadFileActivity")); + EmailNotifier.handle(uploadFile); + + Activity startApp = new Activity(); + startApp.setUsername(testEmail); + startApp.setType(Arrays.asList("Activity", "StartApplicationActivity")); + EmailNotifier.handle(startApp); + + Activity mailApp = new Activity(); + mailApp.setUsername(testEmail); + mailApp.setType(Arrays.asList("Activity", "MailApplicationActivity")); + EmailNotifier.handle(mailApp); + } +} + diff --git a/src/test/EmailTest/SendgridManualTest.java b/src/test/EmailTest/SendgridManualTest.java new file mode 100644 index 00000000..8eaa2f31 --- /dev/null +++ b/src/test/EmailTest/SendgridManualTest.java @@ -0,0 +1,44 @@ +package EmailTest; + + +import com.sendgrid.*; +import com.sendgrid.helpers.mail.Mail; +import com.sendgrid.helpers.mail.objects.Content; +import com.sendgrid.helpers.mail.objects.Email; +import io.github.cdimascio.dotenv.Dotenv; + +import java.io.IOException; + +public class SendgridManualTest { + public static void main(String[] args) { + // manually load .env + Dotenv dotenv = Dotenv.load(); + String SENDGRID_API_KEY = dotenv.get("SENDGRID_API_KEY"); + + if (SENDGRID_API_KEY == null) { + System.err.println("SENDGRID_API_KEY is missing or .env not loaded."); + return; + } + + Email from = new Email("vanessachung@keep.id"); // sender + Email to = new Email("vanessa137222@gmail.com"); + String subject = "Manual Test Email from KeepID"; + Content content = new Content("text/plain", "This is a test email sent directly via SendGrid."); + Mail mail = new Mail(from, subject, to, content); + + SendGrid sg = new SendGrid(SENDGRID_API_KEY); + Request request = new Request(); + + try { + request.setMethod(Method.POST); + request.setEndpoint("mail/send"); + request.setBody(mail.build()); + Response response = sg.api(request); + + System.out.println("Status code: " + response.getStatusCode()); + System.out.println("Response body: " + response.getBody()); + } catch (IOException ex) { + System.err.println("Failed to send email: " + ex.getMessage()); + } + } +} From a647829ca38bd3762a8849f0f7de1c99883f6216 Mon Sep 17 00:00:00 2001 From: vanessachung Date: Wed, 18 Jun 2025 23:24:21 +0800 Subject: [PATCH 12/14] successfully test that user can receive email by diff activity type --- src/main/Mail/Services/SendgridService.java | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/Mail/Services/SendgridService.java b/src/main/Mail/Services/SendgridService.java index 9171c338..59d65ece 100644 --- a/src/main/Mail/Services/SendgridService.java +++ b/src/main/Mail/Services/SendgridService.java @@ -4,12 +4,21 @@ import com.sendgrid.helpers.mail.objects.Content; import com.sendgrid.helpers.mail.objects.Email; import com.sendgrid.helpers.mail.Mail; +import io.github.cdimascio.dotenv.Dotenv; +import java.util.Optional; import java.io.IOException; public class SendgridService { - private static final String SENDGRID_API_KEY = System.getenv("SENDGRID_API_KEY"); + private static final String SENDGRID_API_KEY = + Optional.ofNullable(System.getenv("SENDGRID_API_KEY")) + .orElseGet(() -> Dotenv.configure() + .ignoreIfMissing() + .load() + .get("SENDGRID_API_KEY")); + + //private static final String SENDGRID_API_KEY = System.getenv("SENDGRID_API_KEY"); private static void sendEmail(String toEmail, String subject, String bodyHtml) { Email from = new Email("vanessachung@keep.id"); @@ -37,25 +46,26 @@ private static void sendEmail(String toEmail, String subject, String bodyHtml) { public static void sendWelcomeWithQuickStart(String username, String state) { String subject = "Welcome to Keep.id!"; String body = "

Hi " + username + ", welcome! Here's how to get your ID in " + state + "...

"; - sendEmail(username + "@example.com", subject, body); // Replace with real email + sendEmail(username, subject, body); // Replace with real email } public static void sendUploadReminder(String username, String docType) { String subject = "You uploaded a " + docType; String body = "

We’ve saved your " + docType + ". Here's what to upload next…

"; - sendEmail(username + "@example.com", subject, body); + sendEmail(username, subject, body); } public static void sendApplicationReminder(String username) { - sendEmail(username + "@example.com", "Finish your application", "

You started an application. Come back and finish it!

"); + sendEmail(username , "Finish your application", "

You started an application. Come back and finish it!

"); } public static void sendPickupInfo(String username, String nonprofit) { - sendEmail(username + "@example.com", "Pick up your ID", "

Your documents are ready! Go to " + nonprofit + " to pick them up.

"); + sendEmail(username , "Pick up your ID", "

Your documents are ready! Go to " + nonprofit + " to pick them up.

"); } public static void sendTestEmail() { String subject = "Test from Keep.id"; String body = "

Hello! This is a test email sent via SendGrid!

"; sendEmail("vanessachung@keep.id", subject, body); } + } From 6f6b839eb8f9e251a554c2a68d867b79472dcb9b Mon Sep 17 00:00:00 2001 From: vanessachung Date: Fri, 27 Jun 2025 14:27:50 +0800 Subject: [PATCH 13/14] seperate the email template and adjust act name --- src/main/Mail/EmailNotifier.java | 8 +-- src/main/Mail/Services/SendgridService.java | 63 +++++++++++++++---- src/main/Mail/Utils/TemplateEngine.java | 20 ++++++ .../Mail/email_templates/upload_reminder.html | 1 + src/main/Mail/email_templates/welcome.html | 23 +++++++ src/test/EmailTest/EmailIntTest.java | 30 ++++----- src/test/EmailTest/EmailNotifierTest.java | 16 ++--- src/test/EmailTest/SendgridManualTest.java | 2 +- 8 files changed, 124 insertions(+), 39 deletions(-) create mode 100644 src/main/Mail/Utils/TemplateEngine.java create mode 100644 src/main/Mail/email_templates/upload_reminder.html create mode 100644 src/main/Mail/email_templates/welcome.html diff --git a/src/main/Mail/EmailNotifier.java b/src/main/Mail/EmailNotifier.java index e88415c3..0b60e3f1 100644 --- a/src/main/Mail/EmailNotifier.java +++ b/src/main/Mail/EmailNotifier.java @@ -15,16 +15,16 @@ public static void handle(Activity activity) { System.out.println("Handling activity type: " + type + " for user: " + activity.getUsername()); switch (type) { case "CreateClientActivity": - SendgridService.sendWelcomeWithQuickStart(activity.getUsername(), "PA"); // placeholder + SendgridService.handleCreateClientActivity(activity.getUsername(), "PA"); // placeholder break; case "UploadFileActivity": - SendgridService.sendUploadReminder(activity.getUsername(), "DocumentType"); // placeholder + SendgridService.handleUploadFileActivity(activity.getUsername(), "DocumentType"); // placeholder break; case "StartApplicationActivity": - SendgridService.sendApplicationReminder(activity.getUsername()); + SendgridService.handleMailApplicationActivity(activity.getUsername()); break; case "MailApplicationActivity": - SendgridService.sendPickupInfo(activity.getUsername(), "NonprofitName"); // placeholder + SendgridService.handleSubmitApplicationActivity(activity.getUsername(), "NonprofitName"); // placeholder break; // case "SubmitApplicationActivity": // SendgridService.sendSubmissionConfirmation(user, "ApplicationName"); diff --git a/src/main/Mail/Services/SendgridService.java b/src/main/Mail/Services/SendgridService.java index 59d65ece..d8ea9e78 100644 --- a/src/main/Mail/Services/SendgridService.java +++ b/src/main/Mail/Services/SendgridService.java @@ -5,7 +5,10 @@ import com.sendgrid.helpers.mail.objects.Email; import com.sendgrid.helpers.mail.Mail; import io.github.cdimascio.dotenv.Dotenv; + +import java.util.Map; import java.util.Optional; +import Mail.Utils.TemplateEngine; import java.io.IOException; @@ -42,30 +45,68 @@ private static void sendEmail(String toEmail, String subject, String bodyHtml) { System.err.println("SendGrid Exception: " + ex.getMessage()); } } - - public static void sendWelcomeWithQuickStart(String username, String state) { - String subject = "Welcome to Keep.id!"; - String body = "

Hi " + username + ", welcome! Here's how to get your ID in " + state + "...

"; - sendEmail(username, subject, body); // Replace with real email + // Triggered by CreateClientActivity + public static void handleCreateClientActivity(String username, String state) { + try { + String body = TemplateEngine.renderTemplate("welcome.html", Map.of( + "username", username, + "state", state + )); + sendEmail(username, "Welcome to Keep.id!", body); + } catch (IOException e) { + System.err.println("Failed to load welcome template: " + e.getMessage()); + } } - - public static void sendUploadReminder(String username, String docType) { + // Triggered by UploadFileActivity + public static void handleUploadFileActivity(String username, String docType) { String subject = "You uploaded a " + docType; String body = "

We’ve saved your " + docType + ". Here's what to upload next…

"; sendEmail(username, subject, body); } - public static void sendApplicationReminder(String username) { - sendEmail(username , "Finish your application", "

You started an application. Come back and finish it!

"); + // Triggered by MailApplicationActivity + public static void handleMailApplicationActivity(String username) { + String subject = "Finish your application"; + String body = "

You started an application. Come back and finish it!

"; + sendEmail(username, subject, body); } - public static void sendPickupInfo(String username, String nonprofit) { - sendEmail(username , "Pick up your ID", "

Your documents are ready! Go to " + nonprofit + " to pick them up.

"); + // Triggered by SubmitApplicationActivity + public static void handleSubmitApplicationActivity(String username, String nonprofit) { + String subject = "Pick up your ID"; + String body = "

Your documents are ready! Go to " + nonprofit + " to pick them up.

"; + sendEmail(username, subject, body); } + + // For debugging public static void sendTestEmail() { String subject = "Test from Keep.id"; String body = "

Hello! This is a test email sent via SendGrid!

"; sendEmail("vanessachung@keep.id", subject, body); } +// public static void sendWelcomeWithQuickStart(String username, String state) { +// String subject = "Welcome to Keep.id!"; +// String body = "

Hi " + username + ", welcome! Here's how to get your ID in " + state + "...

"; +// sendEmail(username, subject, body); // Replace with real email +// } +// +// public static void sendUploadReminder(String username, String docType) { +// String subject = "You uploaded a " + docType; +// String body = "

We’ve saved your " + docType + ". Here's what to upload next…

"; +// sendEmail(username, subject, body); +// } +// +// public static void sendApplicationReminder(String username) { +// sendEmail(username , "Finish your application", "

You started an application. Come back and finish it!

"); +// } +// +// public static void sendPickupInfo(String username, String nonprofit) { +// sendEmail(username , "Pick up your ID", "

Your documents are ready! Go to " + nonprofit + " to pick them up.

"); +// } +// public static void sendTestEmail() { +// String subject = "Test from Keep.id"; +// String body = "

Hello! This is a test email sent via SendGrid!

"; +// sendEmail("vanessachung@keep.id", subject, body); +// } } diff --git a/src/main/Mail/Utils/TemplateEngine.java b/src/main/Mail/Utils/TemplateEngine.java new file mode 100644 index 00000000..95e6c717 --- /dev/null +++ b/src/main/Mail/Utils/TemplateEngine.java @@ -0,0 +1,20 @@ +package Mail.Utils; + + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Map; + +public class TemplateEngine { + public static String renderTemplate(String templateName, Map variables) throws IOException { + String templatePath = "src/main/Mail/email_templates/" + templateName; + String content = Files.readString(Paths.get(templatePath)); + + for (Map.Entry entry : variables.entrySet()) { + content = content.replace("{{" + entry.getKey() + "}}", entry.getValue()); + } + + return content; + } +} diff --git a/src/main/Mail/email_templates/upload_reminder.html b/src/main/Mail/email_templates/upload_reminder.html new file mode 100644 index 00000000..ebfb3243 --- /dev/null +++ b/src/main/Mail/email_templates/upload_reminder.html @@ -0,0 +1 @@ +

We’ve saved your {{docType}}. Here's what to upload next…

\ No newline at end of file diff --git a/src/main/Mail/email_templates/welcome.html b/src/main/Mail/email_templates/welcome.html new file mode 100644 index 00000000..56a61828 --- /dev/null +++ b/src/main/Mail/email_templates/welcome.html @@ -0,0 +1,23 @@ + + + + + Welcome to Keep.id! + + +

Welcome, {{username}}!

+

We're excited to have you join Keep.id — your secure and simple way to manage your identity online. +

+ +

To get started with your ID application in {{state}}, here’s what you can do next:

+
    +
  • Complete your profile and required documents
  • +
  • Upload scans or photos of your identification
  • +
  • Pick a nonprofit location for pickup
  • +
+ +

If you have any questions, feel free to reach out to our support team.

+ +

Welcome aboard!
The Keep.id Team

+ + diff --git a/src/test/EmailTest/EmailIntTest.java b/src/test/EmailTest/EmailIntTest.java index 3941ef89..f3e4ce74 100644 --- a/src/test/EmailTest/EmailIntTest.java +++ b/src/test/EmailTest/EmailIntTest.java @@ -9,27 +9,27 @@ public class EmailIntTest { @Test public void testSendEmailsForAllActivities() { - String testEmail = "vanessa137222@gmail.com"; + String testEmail = "vanessachung@keep.id"; Activity createClient = new Activity(); createClient.setUsername(testEmail); createClient.setType(Arrays.asList("Activity", "CreateClientActivity")); EmailNotifier.handle(createClient); - Activity uploadFile = new Activity(); - uploadFile.setUsername(testEmail); - uploadFile.setType(Arrays.asList("Activity", "UploadFileActivity")); - EmailNotifier.handle(uploadFile); - - Activity startApp = new Activity(); - startApp.setUsername(testEmail); - startApp.setType(Arrays.asList("Activity", "StartApplicationActivity")); - EmailNotifier.handle(startApp); - - Activity mailApp = new Activity(); - mailApp.setUsername(testEmail); - mailApp.setType(Arrays.asList("Activity", "MailApplicationActivity")); - EmailNotifier.handle(mailApp); +// Activity uploadFile = new Activity(); +// uploadFile.setUsername(testEmail); +// uploadFile.setType(Arrays.asList("Activity", "UploadFileActivity")); +// EmailNotifier.handle(uploadFile); +// +// Activity submitApp = new Activity(); +// submitApp.setUsername(testEmail); +// submitApp.setType(Arrays.asList("Activity", "SubmitApplicationActivity")); +// EmailNotifier.handle(submitApp); +// +// Activity mailApp = new Activity(); +// mailApp.setUsername(testEmail); +// mailApp.setType(Arrays.asList("Activity", "MailApplicationActivity")); +// EmailNotifier.handle(mailApp); } } diff --git a/src/test/EmailTest/EmailNotifierTest.java b/src/test/EmailTest/EmailNotifierTest.java index 3cd1f82e..3a4b7f75 100644 --- a/src/test/EmailTest/EmailNotifierTest.java +++ b/src/test/EmailTest/EmailNotifierTest.java @@ -26,7 +26,7 @@ public void testCreateClientActivity() { activity.setType(Arrays.asList("Activity", "CreateClientActivity")); try (MockedStatic mocked = mockStatic(SendgridService.class)) { EmailNotifier.handle(activity); - mocked.verify(() -> SendgridService.sendWelcomeWithQuickStart("testuser", "PA")); + mocked.verify(() -> SendgridService.handleCreateClientActivity("testuser", "PA")); } } @@ -35,25 +35,25 @@ public void testUploadFileActivity() { activity.setType(Arrays.asList("Activity", "UploadFileActivity")); try (MockedStatic mocked = mockStatic(SendgridService.class)) { EmailNotifier.handle(activity); - mocked.verify(() -> SendgridService.sendUploadReminder("testuser", "DocumentType")); + mocked.verify(() -> SendgridService.handleUploadFileActivity("testuser", "DocumentType")); } } @Test - public void testStartApplicationActivity() { - activity.setType(Arrays.asList("Activity", "StartApplicationActivity")); + public void testMailApplicationActivity() { + activity.setType(Arrays.asList("Activity", "MailApplicationActivity")); try (MockedStatic mocked = mockStatic(SendgridService.class)) { EmailNotifier.handle(activity); - mocked.verify(() -> SendgridService.sendApplicationReminder("testuser")); + mocked.verify(() -> SendgridService.handleMailApplicationActivity("testuser")); } } @Test - public void testMailApplicationActivity() { - activity.setType(Arrays.asList("Activity", "MailApplicationActivity")); + public void testSubmitApplicationActivity() { + activity.setType(Arrays.asList("Activity", "SubmitApplicationActivity")); try (MockedStatic mocked = mockStatic(SendgridService.class)) { EmailNotifier.handle(activity); - mocked.verify(() -> SendgridService.sendPickupInfo("testuser", "NonprofitName")); + mocked.verify(() -> SendgridService.handleSubmitApplicationActivity("testuser", "NonprofitName")); } } diff --git a/src/test/EmailTest/SendgridManualTest.java b/src/test/EmailTest/SendgridManualTest.java index 8eaa2f31..1229ac8e 100644 --- a/src/test/EmailTest/SendgridManualTest.java +++ b/src/test/EmailTest/SendgridManualTest.java @@ -8,7 +8,7 @@ import io.github.cdimascio.dotenv.Dotenv; import java.io.IOException; - +// ensure sendgrid API is connected public class SendgridManualTest { public static void main(String[] args) { // manually load .env From 60f9d2c313b00fc0461bab7af8615097adc70768 Mon Sep 17 00:00:00 2001 From: vanessachung Date: Mon, 14 Jul 2025 20:12:04 +0800 Subject: [PATCH 14/14] add images into email templates --- src/main/Mail/EmailNotifier.java | 2 +- src/main/Mail/SendgridTest.java | 12 ----- src/main/Mail/email_templates/welcome.html | 63 +++++++++++++++++----- src/test/EmailTest/EmailIntTest.java | 2 + 4 files changed, 54 insertions(+), 25 deletions(-) delete mode 100644 src/main/Mail/SendgridTest.java diff --git a/src/main/Mail/EmailNotifier.java b/src/main/Mail/EmailNotifier.java index 0b60e3f1..4fbceb3f 100644 --- a/src/main/Mail/EmailNotifier.java +++ b/src/main/Mail/EmailNotifier.java @@ -12,7 +12,7 @@ public static void handle(Activity activity) { if (types == null || types.isEmpty()) return; String type = types.get(types.size() - 1); // get the most specific activity type - System.out.println("Handling activity type: " + type + " for user: " + activity.getUsername()); + // System.out.println("Handling activity type: " + type + " for user: " + activity.getUsername()); switch (type) { case "CreateClientActivity": SendgridService.handleCreateClientActivity(activity.getUsername(), "PA"); // placeholder diff --git a/src/main/Mail/SendgridTest.java b/src/main/Mail/SendgridTest.java deleted file mode 100644 index a799c0bc..00000000 --- a/src/main/Mail/SendgridTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package Mail; - -import Mail.Services.SendgridService; - - -public class SendgridTest { - public static void main(String[] args) { - // System.out.println("SENDGRID_API_KEY = " + System.getenv("SENDGRID_API_KEY")); - SendgridService.sendTestEmail(); - } -} - diff --git a/src/main/Mail/email_templates/welcome.html b/src/main/Mail/email_templates/welcome.html index 56a61828..baae1b48 100644 --- a/src/main/Mail/email_templates/welcome.html +++ b/src/main/Mail/email_templates/welcome.html @@ -4,20 +4,59 @@ Welcome to Keep.id! - -

Welcome, {{username}}!

-

We're excited to have you join Keep.id — your secure and simple way to manage your identity online. -

+ + + + + + -

To get started with your ID application in {{state}}, here’s what you can do next:

-
    -
  • Complete your profile and required documents
  • -
  • Upload scans or photos of your identification
  • -
  • Pick a nonprofit location for pickup
  • -
+ +
+

+ + Welcome, {{username}}! +

+

We're excited to have you join Keep.id — your secure and simple way to manage your identity + online.

-

If you have any questions, feel free to reach out to our support team.

+

To get started with your ID application in {{state}}, here’s what you can do next:

-

Welcome aboard!
The Keep.id Team

+ +
    +
  • + + Complete your profile and required documents +
  • +
  • + + Upload scans or photos of your identification +
  • +
  • + + Pick a nonprofit location for pickup +
  • +
+ +

If you have any questions, feel free to reach out to our support team.

+ + +
+ 2025-07-14-7-57-24 +

We’re here to help you every step of the way.

+
+ +

Welcome aboard!
The Keep.id Team

+
+ + +
+ © {2020} Keep.id – All rights reserved. +
diff --git a/src/test/EmailTest/EmailIntTest.java b/src/test/EmailTest/EmailIntTest.java index f3e4ce74..e031721c 100644 --- a/src/test/EmailTest/EmailIntTest.java +++ b/src/test/EmailTest/EmailIntTest.java @@ -9,6 +9,8 @@ public class EmailIntTest { @Test public void testSendEmailsForAllActivities() { + + // change the email to test String testEmail = "vanessachung@keep.id"; Activity createClient = new Activity();