Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
45f35f8
Adding notif email generation and sending to the iapi
jacob6838 Jan 6, 2026
c8cbeeb
Adding notification email examples
jacob6838 Jan 6, 2026
3d068d4
Adding iapi email controller
jacob6838 Jan 6, 2026
169105e
Fixing EmailServiceTest
jacob6838 Jan 9, 2026
6987b60
Update EmailController.java
jacob6838 Jan 9, 2026
4d4fce5
Update EmailServiceTest.java
jacob6838 Jan 9, 2026
1cb6ae9
Fixing broken config property refs
jacob6838 Jan 9, 2026
7100784
Adding prototype unsubscription page and api endpoint
jacob6838 Jan 10, 2026
56e42ae
Adding descriptions and required roles to email subscriptions
jacob6838 Jan 12, 2026
2e21c67
Adding manage subscriptions endpoint
jacob6838 Jan 13, 2026
7efcf26
Working email unsubscription through API
jacob6838 Jan 13, 2026
7dd1294
Adding unit tests for email components
jacob6838 Jan 13, 2026
6e251a4
Adding EmailType default constructor
jacob6838 Jan 13, 2026
11f03f8
Removing KC authentication from unsubscribe page
jacob6838 Jan 14, 2026
8361e8e
Updating email generator comments, colors, and content
jacob6838 Jan 14, 2026
d4deef4
Updating default iapi env vars to avoid localhost
jacob6838 Jan 14, 2026
b5411d8
Update EmailRecipient.java
jacob6838 Jan 14, 2026
9eebbe7
Adding email frequency to tables and queries
jacob6838 Jan 14, 2026
4b79219
Update launch.json
jacob6838 Jan 14, 2026
648b367
Improving getCombinedResponseEntity to handle partial failures
jacob6838 Jan 14, 2026
9990c6f
Merge branch 'intersection-api-email-generation-1' into iapi-email-un…
jacob6838 Jan 14, 2026
d86d53e
Update UserControllerTest.java
jacob6838 Jan 14, 2026
f748fb6
Removing secure env vars from application.yaml
jacob6838 Jan 15, 2026
06338b1
Renaming email template to email_template.html
jacob6838 Jan 15, 2026
9c89f37
Removing duplicate dependency
jacob6838 Jan 15, 2026
c7676b9
Update services/intersection-api/api/src/main/java/us/dot/its/jpo/ode…
jacob6838 Jan 15, 2026
0a3330b
Handling unsubscribe URL generation errors
jacob6838 Jan 15, 2026
84c8cbe
Merge branch 'intersection-api-email-generation-1' of https://github.…
jacob6838 Jan 15, 2026
f1e79bc
Removing unused methods
jacob6838 Jan 15, 2026
f00d7f2
Cleaning up email generation code
jacob6838 Jan 15, 2026
6825d8d
Writing email provider tests
jacob6838 Jan 15, 2026
552cc70
Renaming unsubscribe controller
jacob6838 Jan 15, 2026
28f739e
Working SubscriptionController
jacob6838 Jan 15, 2026
aa92f59
Merge branch 'intersection-api-email-generation-1' into iapi-email-un…
jacob6838 Jan 15, 2026
bde2dcc
Adding unsubscriptionSlice
jacob6838 Jan 15, 2026
a6ee962
Implementing subscription management page in cvmanager
jacob6838 Jan 15, 2026
b2f0cd3
Fixing unit tests
jacob6838 Jan 16, 2026
791aa48
Merge branch 'develop' into intersection-api-email-generation-1
jacob6838 Jan 19, 2026
bf13505
Merge branch 'intersection-api-email-generation-1' into iapi-email-un…
jacob6838 Jan 19, 2026
85bf31a
Merge branch 'develop' into iapi-email-unsubscription
jacob6838 Apr 15, 2026
13da463
Migrating PostgresService to Repositories
jacob6838 Apr 17, 2026
a2c34fc
Updating EmailService tests
jacob6838 Apr 17, 2026
df83a0a
Merge branch 'develop' into iapi-email-unsubscription
jacob6838 Apr 17, 2026
9becb12
Resolving subscription ui typescript errors
jacob6838 Apr 20, 2026
ad82998
Syncing unsubscribe and manage subscriptions pages
jacob6838 Apr 20, 2026
dc32426
Delete EmailSubscriptionsTable.tsx
jacob6838 Apr 20, 2026
3ce05e4
Removing AdminNotification pages
jacob6838 Apr 20, 2026
ca464e4
Adding hikari to application-integration-test.yaml
jacob6838 Apr 20, 2026
18ff475
Removing adminNotification webapp references
jacob6838 Apr 20, 2026
907b9e3
Filtering email subscriptions by required role
jacob6838 Apr 20, 2026
baaa9e4
Enabling notif subscription page feedback
jacob6838 Apr 21, 2026
cf0639a
Adding BrowserRouter to notif sub tests
jacob6838 Apr 21, 2026
4850dc6
Update SubscriptionForm.test.tsx
jacob6838 Apr 21, 2026
a57dc52
Create SubscriptionForm.test.tsx.snap
jacob6838 Apr 21, 2026
343cce3
Cleaning up
jacob6838 Apr 21, 2026
f816b02
Removing client-side role filtering from notif sub page
jacob6838 Apr 21, 2026
7f46fde
Adding isModified detection to notif sub form
jacob6838 Apr 21, 2026
3a76b93
Rewriting email_type sql update script
jacob6838 Apr 21, 2026
cb22010
Handling notif sub form parent updates with prevSubscriptions
jacob6838 Apr 21, 2026
3457329
Create email_type_required_role.sql
jacob6838 Apr 21, 2026
572aa81
Writing EmailServiceTest without EqualsAndHashCode
jacob6838 Apr 21, 2026
08331da
Merge branch 'develop' into iapi-email-unsubscription
jacob6838 Apr 21, 2026
d65f8d8
Updating exception and return types in notif sub endpoints
jacob6838 Apr 21, 2026
e205db0
Validating notif api requests
jacob6838 Apr 21, 2026
d90db48
Updating notif sub controller tests
jacob6838 Apr 21, 2026
52843d3
Merge branch 'develop' into iapi-email-unsubscription
jacob6838 Apr 27, 2026
601f6ef
Merge branch 'develop' into iapi-email-unsubscription
jacob6838 Apr 27, 2026
930ba8f
Removing global exception handler 500 catch-all
jacob6838 Apr 27, 2026
285a29a
Converting SubscriptionController tests to MVC
jacob6838 Apr 27, 2026
66b12fc
Developing unsubscribe controller mvc tests
jacob6838 Apr 27, 2026
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
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
"timefield",
"toaddrs",
"txrxmsg",
"unsubscription",
"upgrader",
"usdot",
"usdotjpoode",
Expand Down
2 changes: 1 addition & 1 deletion docker-compose-intersection.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ services:
UNSUBSCRIBE_SECRET_KEY: ${UNSUBSCRIBE_SECRET_KEY:-b5f9070ca58d4576ea6a2d8d4c1b97caf8abc2554198f8f9acf86d0d31a52a52}
CV_MANAGER_FRONT_END_URI: ${CV_MANAGER_FRONT_END_URI:-http://localhost:${WEBAPP_PORT:-3000}}
INTERSECTION_SMTP_SERVER_HOST: ${INTERSECTION_SMTP_SERVER_HOST:-smtp4dev}
INTERSECTION_SMTP_SERVER_PORT: ${INTERSECTION_SMTP_SERVER_PORT:-1025}
INTERSECTION_SMTP_SERVER_PORT: ${INTERSECTION_SMTP_SERVER_PORT:-25}
INTERSECTION_SMTP_USERNAME: ${INTERSECTION_SMTP_USERNAME:-admin}
INTERSECTION_SMTP_PASSWORD: ${INTERSECTION_SMTP_PASSWORD:-password}
INTERSECTION_SMTP_AUTH: ${INTERSECTION_SMTP_AUTH_ENABLED:-true}
Expand Down
5 changes: 5 additions & 0 deletions resources/sql_scripts/CVManager_CreateTables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -430,11 +430,16 @@ CREATE TABLE IF NOT EXISTS public.email_type
email_type_id integer NOT NULL DEFAULT nextval('email_type_email_type_id_seq'::regclass),
email_type character varying(128) COLLATE pg_catalog.default NOT NULL,
description character varying(256) COLLATE pg_catalog.default,
required_role integer NOT NULL,
supports_immediate boolean DEFAULT true NOT NULL,
supports_hourly boolean DEFAULT false NOT NULL,
supports_daily boolean DEFAULT false NOT NULL,
supports_weekly boolean DEFAULT false NOT NULL,
supports_monthly boolean DEFAULT false NOT NULL,
CONSTRAINT fk_role_id FOREIGN KEY (required_role)
REFERENCES public.roles (role_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT email_type_pkey PRIMARY KEY (email_type_id),
CONSTRAINT email_type_unique UNIQUE (email_type),
CONSTRAINT at_least_one_frequency CHECK (supports_immediate OR supports_hourly OR supports_daily OR supports_weekly OR supports_monthly)
Expand Down
16 changes: 8 additions & 8 deletions resources/sql_scripts/CVManager_SampleData.sql
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,19 @@ INSERT INTO public.snmp_msgfwd_config(
(2, 3, 2, 'SPAT', '10.0.0.80', 44910, '2024/04/01T00:00:00', '2034/04/01T00:00:00', '1', '0');

INSERT INTO public.email_type(
email_type, supports_immediate, supports_hourly, supports_daily, supports_weekly, supports_monthly)
VALUES ('Support Requests', true, false, false, false, false),
('Firmware Upgrade Failures', true, false, false, false, false),
('Daily Message Counts', true, false, false, false, false),
('Access Requests', true, false, false, false, false),
('Intersection Notification Summary', true, true, true, true, true),
('Critical Error Messages', true, false, false, false, false);
email_type, required_role, description, supports_immediate, supports_hourly, supports_daily, supports_weekly, supports_monthly)
VALUES ('Support Requests', 1, 'Receive support requests from users', true, false, false, false, false),
('Firmware Upgrade Failures', 2, 'Receive automated firmware upgrade failure emails', true, false, false, false, false),
('Daily Message Counts', 3, 'Receive automated daily message count emails', false, true, false, false, false),
('Access Requests', 1, 'Receive organization access requests from users', true, false, false, false, false),
('Intersection Notification Summary', 3, 'Receive automated intersection notification summary emails', true, true, true, true, true),
('Critical Error Messages', 2, 'Receive automated critical error message emails', true, false, false, false, false);

INSERT INTO public.user_email_notification(
user_email_notification_id, user_id, email_type_id, immediate, hourly, daily, weekly, monthly)
VALUES (1, 1, 1, true, false, false, false, false),
(2, 1, 2, true, false, false, false, false),
(3, 1, 3, true, false, false, false, false),
(3, 1, 3, false, true, false, false, false),
(4, 1, 4, true, false, false, false, false),
(5, 1, 5, true, true, true, true, true),
(6, 1, 6, true, false, false, false, false);
Expand Down
35 changes: 35 additions & 0 deletions resources/sql_scripts/update_scripts/email_type_required_role.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
-- Update public.email_type table definition
-- omit NOT NULL constraint on required_role for now to allow for smooth transition, will set to NOT NULL after backfilling data
ALTER TABLE public.email_type
ADD COLUMN IF NOT EXISTS required_role integer;

-- Add foreign key constraint
ALTER TABLE public.email_type
ADD CONSTRAINT IF NOT EXISTS fk_role_id FOREIGN KEY (required_role)
REFERENCES public.roles (role_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION;
Comment on lines +7 to +11
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostgreSQL supports ADD COLUMN IF NOT EXISTS, but it does not support ALTER TABLE ... ADD CONSTRAINT IF NOT EXISTS ... in many commonly used Postgres versions. This migration may fail at runtime. Consider guarding constraint creation with a DO $$ ... IF NOT EXISTS (SELECT 1 FROM pg_constraint ...) THEN ... END IF; $$ block, or create the constraint unconditionally in a fresh migration where you can ensure it doesn’t already exist.

Suggested change
ALTER TABLE public.email_type
ADD CONSTRAINT IF NOT EXISTS fk_role_id FOREIGN KEY (required_role)
REFERENCES public.roles (role_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint c
JOIN pg_class t ON t.oid = c.conrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE c.conname = 'fk_role_id'
AND t.relname = 'email_type'
AND n.nspname = 'public'
) THEN
ALTER TABLE public.email_type
ADD CONSTRAINT fk_role_id FOREIGN KEY (required_role)
REFERENCES public.roles (role_id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION;
END IF;
END $$;

Copilot uses AI. Check for mistakes.

-- Set default value for all existing entries to role_id 1 (ADMIN)
UPDATE public.email_type
SET required_role = 1
WHERE required_role IS NULL;

-- ADMIN roles
UPDATE public.email_type
SET required_role = 1
WHERE email_type IN ('Support Requests', 'Access Requests');

-- OPERATOR roles
UPDATE public.email_type
SET required_role = 2
WHERE email_type IN ('Firmware Upgrade Failures', 'Critical Error Messages');

-- USER roles
UPDATE public.email_type
SET required_role = 3
WHERE email_type IN ('Daily Message Counts', 'Intersection Notification Summary');

-- Make the column NOT NULL after setting all values
ALTER TABLE public.email_type
ALTER COLUMN required_role SET NOT NULL;
Original file line number Diff line number Diff line change
Expand Up @@ -268,17 +268,6 @@ public ErrorResponse handleServletRequestBinding(ServletRequestBindingException
return ErrorResponse.builder(ex, problemDetail).build();
}

@ExceptionHandler()
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleException(Exception ex) {
log.error("Unexpected server error:", ex);
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(
HttpStatus.INTERNAL_SERVER_ERROR,
"An unexpected error occurred. Please try again later.");
var errorRes = ErrorResponse.builder(ex, problemDetail);
return errorRes.build();
}

private String buildUserFriendlyMessage(String message, DataIntegrityViolationException ex) {
if (message == null) {
return "A database constraint was violated. Please check your input and try again.";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package us.dot.its.jpo.ode.api.controllers.users;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import us.dot.its.jpo.ode.api.models.emails.UserEmailNotificationDto;
import us.dot.its.jpo.ode.api.models.keycloak.CvManagerAuthToken;
import us.dot.its.jpo.ode.api.models.UserRole;
import us.dot.its.jpo.ode.api.models.emails.EmailSubscriptionGetResponse;
import us.dot.its.jpo.ode.api.services.EmailService;
import us.dot.its.jpo.ode.api.services.PermissionService;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestBody;

@Slf4j
@RestController
@ConditionalOnProperty(name = "enable.api", havingValue = "true", matchIfMissing = false)
@ApiResponses(value = {
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "500", description = "Internal Server Error")
})
@RequestMapping("/users/subscriptions")
@RequiredArgsConstructor
public class SubscriptionController {
private final EmailService emailService;
private final PermissionService permissionService;

@RequestMapping(value = "/email-subscriptions", method = RequestMethod.GET, produces = "application/json")
@PreAuthorize("@PermissionService.isSuperUser() || @PermissionService.hasRole('USER')")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "400", description = "Invalid message body"),
})
public EmailSubscriptionGetResponse getEmailSubscriptions() {

CvManagerAuthToken authToken = permissionService.getCvManagerAuthToken();
boolean isOperator = !authToken.getQualifiedOrgList(UserRole.OPERATOR).isEmpty();
boolean isAdmin = !authToken.getQualifiedOrgList(UserRole.ADMIN).isEmpty();
List<UserEmailNotificationDto> subscriptions = emailService.getAllEmailSubscriptionOptionsForUser(
authToken.getEmail(),
isOperator, isAdmin);
return new EmailSubscriptionGetResponse(subscriptions, authToken.getEmail());
}

@Operation(summary = "Update email subscription preferences", description = "Update the user's email subscription preferences")
@RequestMapping(value = "/email-subscriptions", method = RequestMethod.POST, produces = "application/json")
@PreAuthorize("@PermissionService.isSuperUser() || @PermissionService.hasRole('USER')")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "400", description = "Invalid message body"),
})
@ResponseStatus(HttpStatus.OK)
public void updateEmailSubscriptions(
@Valid @RequestBody List<UserEmailNotificationDto> requestedSubscriptions) {

CvManagerAuthToken authToken = permissionService.getCvManagerAuthToken();

emailService.updateEmailSubscriptions(authToken.getEmail(), requestedSubscriptions);

return;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package us.dot.its.jpo.ode.api.controllers.users;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

import javax.ws.rs.NotAuthorizedException;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import us.dot.its.jpo.ode.api.emails.UnsubscribeTokenGenerator;
import us.dot.its.jpo.ode.api.models.emails.UserEmailNotificationDto;
import us.dot.its.jpo.ode.api.models.postgres.tables.UserOrganization;
import us.dot.its.jpo.ode.api.repositories.UserOrganizationRepository;
import us.dot.its.jpo.ode.api.models.UserRole;
import us.dot.its.jpo.ode.api.models.emails.EmailSubscriptionGetResponse;
import us.dot.its.jpo.ode.api.services.EmailService;

import org.springframework.web.bind.annotation.RequestBody;

@Slf4j
@RestController
@ConditionalOnProperty(name = "enable.api", havingValue = "true", matchIfMissing = false)
@ApiResponses(value = {
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "500", description = "Internal Server Error")
})
@RequestMapping("/users/unsubscribe")
@RequiredArgsConstructor
public class UnsubscribeController {
private final EmailService emailService;
private final UserOrganizationRepository userOrganizationRepository;
private final UnsubscribeTokenGenerator unsubscribeTokenGenerator;

@RequestMapping(value = "/email-subscriptions", method = RequestMethod.GET, produces = "application/json")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "400", description = "Invalid message body"),
})
public EmailSubscriptionGetResponse getEmailSubscriptions(
@RequestParam(required = false) String token) {
String userEmail = unsubscribeTokenGenerator.parseAndValidateToken(token);
if (userEmail == null) {
throw new NotAuthorizedException("Invalid or expired token");
}

List<UserOrganization> userOrganizations = userOrganizationRepository.findAllByEmail(userEmail);
boolean isOperator = userOrganizations.stream()
.anyMatch(org -> UserRole.OPERATOR.equals(UserRole.fromString(org.getRole().getName())));
boolean isAdmin = userOrganizations.stream()
.anyMatch(org -> UserRole.ADMIN.equals(UserRole.fromString(org.getRole().getName())));

List<UserEmailNotificationDto> subscriptions = emailService.getAllEmailSubscriptionOptionsForUser(userEmail,
isOperator, isAdmin);
return new EmailSubscriptionGetResponse(subscriptions, userEmail);
}

@Operation(summary = "Update email subscription preferences", description = "Update the user's email subscription preferences")
@RequestMapping(value = "/email-subscriptions", method = RequestMethod.POST, produces = "application/json")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "400", description = "Invalid message body"),
})
public void updateEmailSubscriptions(
@RequestParam(required = false) String token,
@Valid @RequestBody List<UserEmailNotificationDto> requestedSubscriptions) {
String userEmail = unsubscribeTokenGenerator.parseAndValidateToken(token);
if (userEmail == null) {
throw new NotAuthorizedException("Invalid or expired token");
}

emailService.updateEmailSubscriptions(userEmail, requestedSubscriptions);

return;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(request -> request
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() // Allow CORS preflight
.requestMatchers(HttpMethod.POST, "/users/unsubscribe/email-subscriptions").permitAll()
.requestMatchers(HttpMethod.GET, "/users/unsubscribe/email-subscriptions").permitAll()
.requestMatchers("/**").access(AccessController::checkAccess)
.anyRequest().authenticated())
.oauth2ResourceServer(resourceServerConfigurer -> resourceServerConfigurer.jwt(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package us.dot.its.jpo.ode.api.mappers;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;

import us.dot.its.jpo.ode.api.models.emails.UserEmailNotificationDto;
import us.dot.its.jpo.ode.api.models.postgres.tables.EmailType;
import us.dot.its.jpo.ode.api.models.postgres.tables.UserEmailNotification;

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface UserEmailNotificationMapper {

/**
* Convert UserEmailNotification entity to UserEmailNotificationDto
* MapStruct will automatically map fields with the same name
*/
@Mapping(source = "emailType.emailType", target = "category")
@Mapping(source = "emailType.description", target = "description")
@Mapping(source = "emailType.requiredRole.name", target = "requiredRole")
@Mapping(source = "emailType.supportsImmediate", target = "supportsImmediate")
@Mapping(source = "emailType.supportsHourly", target = "supportsHourly")
@Mapping(source = "emailType.supportsDaily", target = "supportsDaily")
@Mapping(source = "emailType.supportsWeekly", target = "supportsWeekly")
@Mapping(source = "emailType.supportsMonthly", target = "supportsMonthly")
UserEmailNotificationDto toDto(UserEmailNotification notification);

// category, immediate, hourly, daily, weekly, monthly
@Mapping(source = "emailType", target = "category")
@Mapping(source = "requiredRole.name", target = "requiredRole")
@Mapping(target = "immediate", constant = "false")
@Mapping(target = "hourly", constant = "false")
@Mapping(target = "daily", constant = "false")
@Mapping(target = "weekly", constant = "false")
@Mapping(target = "monthly", constant = "false")
UserEmailNotificationDto fromEmailType(EmailType emailType);

// id, user, emailType
@Mapping(target = "id", ignore = true) // Never set id when creating/updating - it's auto-generated
@Mapping(target = "user", ignore = true) // set in service layer
@Mapping(target = "emailType", ignore = true) // set in service layer
UserEmailNotification toEntity(UserEmailNotificationDto dto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package us.dot.its.jpo.ode.api.models.emails;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class EmailSubscriptionGetResponse {
private List<UserEmailNotificationDto> subscriptions;
private String email;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package us.dot.its.jpo.ode.api.models.emails;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class ManageSubscriptionsBody {
private String email;
private List<EmailCategory> categoriesToSubscribe;
}
Loading
Loading