From d50749adfc1fdd48eca64b51700da5da29a06bf6 Mon Sep 17 00:00:00 2001 From: gkhaavik Date: Wed, 7 May 2025 22:15:34 +0200 Subject: [PATCH 1/8] update environment configuration for frontend and database settings --- deploy/.env.example | 6 ++---- deploy/docker-compose.yml | 11 +++++------ src/main/resources/application-docker.properties | 2 +- src/main/resources/application.properties | 3 ++- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/deploy/.env.example b/deploy/.env.example index c1fcff0..a1995c3 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -2,9 +2,6 @@ DATASOURCE_URL=jdbc:mysql://mysql:3306/commercifydb?createDatabaseIfNotExist=true DATASOURCE_USERNAME=commercifyapp DATASOURCE_PASSWORD=password123! -STRIPE_SECRET_TEST_KEY= -STRIPE_WEBHOOK_SECRET= -STRIPE_WEBHOOK_ENDPOINT=https:///api/v2/payments/webhooks/stripe/callback JWT_SECRET_KEY= ADMIN_EMAIL=admin@commercify.app ADMIN_PASSWORD=admin @@ -18,4 +15,5 @@ MOBILEPAY_WEBHOOK_CALLBACK=https:///api/v2/payments/webhooks/mobile MAIL_USERNAME= MAIL_PASSWORD= MAIL_HOST=smtp.ethereal.email -MAIL_PORT=587 \ No newline at end of file +MAIL_PORT=587 +FRONTEND_HOST=http://localhost:3000 \ No newline at end of file diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 3769602..14c8330 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -8,7 +8,7 @@ services: - MYSQL_PASSWORD=password123! - MYSQL_ROOT_PASSWORD=rootpassword ports: - - "3307:3306" + - "3306:3306" healthcheck: test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ] timeout: 5s @@ -26,11 +26,9 @@ services: - "6091:6091" environment: - SPRING_PROFILES_ACTIVE=docker - - SPRING_DATASOURCE_URL=${DATASOURCE_URL} - - SPRING_DATASOURCE_USERNAME=${DATASOURCE_USERNAME} - - SPRING_DATASOURCE_PASSWORD=${DATASOURCE_PASSWORD} - - STRIPE_SECRET_TEST_KEY=${STRIPE_SECRET_TEST_KEY} - - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET} + - SPRING_DATASOURCE_URL=jdbc:mysql://host.docker.internal:3306/commercifydb?createDatabaseIfNotExist=true + - SPRING_DATASOURCE_USERNAME=root + - SPRING_DATASOURCE_PASSWORD=rootpassword - JWT_SECRET_KEY=${JWT_SECRET_KEY} - ADMIN_EMAIL=${ADMIN_EMAIL} - ADMIN_PASSWORD=${ADMIN_PASSWORD} @@ -45,6 +43,7 @@ services: - MAIL_PORT=${MAIL_PORT} - MAIL_USERNAME=${MAIL_USERNAME} - MAIL_PASSWORD=${MAIL_PASSWORD} + - FRONTEND_URL=${FRONTEND_URL} depends_on: mysql: condition: service_healthy diff --git a/src/main/resources/application-docker.properties b/src/main/resources/application-docker.properties index 56b4d37..089311c 100644 --- a/src/main/resources/application-docker.properties +++ b/src/main/resources/application-docker.properties @@ -33,6 +33,6 @@ spring.mail.username=${MAIL_USERNAME} spring.mail.password=${MAIL_PASSWORD} spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true -frontend.host=${FRONTEND_HOST} +frontend.host=${FRONTEND_HOST:http://localhost:3000} # Application Configuration #logging.level.org.springframework.security=debug \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3ad4c31..3b74d71 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -33,6 +33,7 @@ spring.mail.username=${MAIL_USERNAME} spring.mail.password=${MAIL_PASSWORD} spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true -frontend.host=${FRONTEND_HOST} +frontend.host=${FRONTEND_HOST:http://localhost:3000} + # Application Configuration #logging.level.org.springframework.security=debug \ No newline at end of file From 4a4768c82297e52fa1e4eb7d1600669c62e2899b Mon Sep 17 00:00:00 2001 From: gkhaavik Date: Wed, 7 May 2025 22:17:12 +0200 Subject: [PATCH 2/8] refactor user roles to use ROLE_ prefix and implement user profile response DTO --- pom.xml | 3 +- .../commercify/api/auth/AuthController.java | 27 +++++++ .../api/auth/dto/response/AuthResponse.java | 21 +----- .../auth/dto/response/NextAuthResponse.java | 13 ++-- .../commercify/api/users/UserController.java | 70 +++++++++++++++++++ .../AuthenticationApplicationService.java | 8 ++- .../service/AuthenticationResult.java | 4 +- .../auth/domain/model/UserRole.java | 3 +- .../dto/request/PagedUserResponse.java | 12 ++++ .../dto/response/UserProfileResponse.java | 45 ++++++++++++ .../service/UserApplicationService.java | 2 +- .../commercify/user/domain/model/User.java | 2 +- .../user/domain/model/UserRole.java | 8 +-- .../config/AdminUserLoader.java | 2 +- 14 files changed, 178 insertions(+), 42 deletions(-) create mode 100644 src/main/java/com/zenfulcode/commercify/api/users/UserController.java create mode 100644 src/main/java/com/zenfulcode/commercify/user/application/dto/request/PagedUserResponse.java create mode 100644 src/main/java/com/zenfulcode/commercify/user/application/dto/response/UserProfileResponse.java diff --git a/pom.xml b/pom.xml index 9eec35d..50abac2 100644 --- a/pom.xml +++ b/pom.xml @@ -194,7 +194,8 @@ jackson2 module - com.zenfulcode.commercify.api.**.dto.** + + com.zenfulcode.commercify.**.dto.** diff --git a/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java b/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java index f73573c..a17ad7a 100644 --- a/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java +++ b/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java @@ -10,7 +10,9 @@ import com.zenfulcode.commercify.auth.domain.exception.InvalidAuthenticationException; import com.zenfulcode.commercify.auth.domain.model.AuthenticatedUser; import com.zenfulcode.commercify.shared.interfaces.ApiResponse; +import com.zenfulcode.commercify.user.application.dto.response.UserProfileResponse; import com.zenfulcode.commercify.user.application.service.UserApplicationService; +import com.zenfulcode.commercify.user.domain.model.User; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -24,6 +26,7 @@ public class AuthController { private final AuthenticationApplicationService authService; private final UserApplicationService userService; + private final UserApplicationService userApplicationService; @PostMapping("/nextauth") public ResponseEntity> nextAuthSignIn(@RequestBody LoginRequest request) { @@ -52,10 +55,34 @@ public ResponseEntity> validateSession(@RequestHea } } + @GetMapping("/me") + public ResponseEntity> getCurrentUser(@RequestHeader("Authorization") String authHeader) { + System.out.println("Authorization Header: " + authHeader); + + // Extract token using a domain service method + String token = authService.extractTokenFromHeader(authHeader).orElseThrow(() -> new InvalidAuthenticationException("Invalid authorization header")); + + // Validate token through the application service + AuthenticatedUser authenticatedUser = authService.validateAccessToken(token); + + // Fetch full user entity from the database + User user = userApplicationService.getUser(authenticatedUser.getUserId()); + + // Map to response DTO + UserProfileResponse response = UserProfileResponse.fromUser(user); + + return ResponseEntity.ok(ApiResponse.success(response)); + } + @PostMapping("/signin") public ResponseEntity> login(@RequestBody LoginRequest request) { + + System.out.println("Login request: " + request); AuthenticationResult result = authService.authenticate(request.toCommand()); + System.out.println("Authentication result: " + result); + + AuthResponse response = AuthResponse.from(result); return ResponseEntity.ok(ApiResponse.success(response)); } diff --git a/src/main/java/com/zenfulcode/commercify/api/auth/dto/response/AuthResponse.java b/src/main/java/com/zenfulcode/commercify/api/auth/dto/response/AuthResponse.java index c299a80..7d3d3ba 100644 --- a/src/main/java/com/zenfulcode/commercify/api/auth/dto/response/AuthResponse.java +++ b/src/main/java/com/zenfulcode/commercify/api/auth/dto/response/AuthResponse.java @@ -1,33 +1,18 @@ package com.zenfulcode.commercify.api.auth.dto.response; import com.zenfulcode.commercify.auth.application.service.AuthenticationResult; -import com.zenfulcode.commercify.auth.domain.model.UserRole; - -import java.util.Set; -import java.util.stream.Collectors; +import com.zenfulcode.commercify.user.application.dto.response.UserProfileResponse; public record AuthResponse( String accessToken, String refreshToken, - String tokenType, - String userId, - String username, - String email, - Set roles + UserProfileResponse user ) { public static AuthResponse from(AuthenticationResult result) { - Set roles = result.user().getRoles().stream() - .map(UserRole::name) - .collect(Collectors.toSet()); - return new AuthResponse( result.accessToken(), result.refreshToken(), - "Bearer", - result.user().getUserId().toString(), - result.user().getUsername(), - result.user().getEmail(), - roles + UserProfileResponse.fromUser(result.userInfo()) ); } } \ No newline at end of file diff --git a/src/main/java/com/zenfulcode/commercify/api/auth/dto/response/NextAuthResponse.java b/src/main/java/com/zenfulcode/commercify/api/auth/dto/response/NextAuthResponse.java index 25b1a14..149eae4 100644 --- a/src/main/java/com/zenfulcode/commercify/api/auth/dto/response/NextAuthResponse.java +++ b/src/main/java/com/zenfulcode/commercify/api/auth/dto/response/NextAuthResponse.java @@ -1,5 +1,6 @@ package com.zenfulcode.commercify.api.auth.dto.response; +import com.zenfulcode.commercify.user.application.dto.response.UserProfileResponse; import com.zenfulcode.commercify.auth.application.service.AuthenticationResult; import com.zenfulcode.commercify.auth.domain.model.AuthenticatedUser; import com.zenfulcode.commercify.auth.domain.model.UserRole; @@ -7,9 +8,7 @@ import java.util.Set; public record NextAuthResponse( - String id, - String name, - String email, + UserProfileResponse user, String accessToken, String refreshToken, Set roles @@ -17,9 +16,7 @@ public record NextAuthResponse( public static NextAuthResponse from(AuthenticationResult result) { AuthenticatedUser user = result.user(); return new NextAuthResponse( - user.getUserId().toString(), - user.getUsername(), - user.getEmail(), + UserProfileResponse.fromUser(result.userInfo()), result.accessToken(), result.refreshToken(), user.getRoles() @@ -28,9 +25,7 @@ public static NextAuthResponse from(AuthenticationResult result) { public static NextAuthResponse fromUser(AuthenticatedUser user) { return new NextAuthResponse( - user.getUserId().toString(), - user.getUsername(), - user.getEmail(), + UserProfileResponse.fromAuthenticatedUser(user), null, null, user.getRoles() diff --git a/src/main/java/com/zenfulcode/commercify/api/users/UserController.java b/src/main/java/com/zenfulcode/commercify/api/users/UserController.java new file mode 100644 index 0000000..1992a55 --- /dev/null +++ b/src/main/java/com/zenfulcode/commercify/api/users/UserController.java @@ -0,0 +1,70 @@ +package com.zenfulcode.commercify.api.users; + +import com.zenfulcode.commercify.api.product.dto.response.PageInfo; +import com.zenfulcode.commercify.shared.interfaces.ApiResponse; +import com.zenfulcode.commercify.user.application.dto.request.PagedUserResponse; +import com.zenfulcode.commercify.user.application.dto.response.UserProfileResponse; +import com.zenfulcode.commercify.user.application.service.UserApplicationService; +import com.zenfulcode.commercify.user.domain.model.User; +import com.zenfulcode.commercify.user.domain.valueobject.UserId; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/v2/users") +@RequiredArgsConstructor +public class UserController { + private final UserApplicationService userApplicationService; + + @GetMapping + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity> getAllUsers( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size, + @RequestParam(defaultValue = "id") String sortBy, + @RequestParam(defaultValue = "DESC") String sortDirection) { + + Sort.Direction direction = Sort.Direction.fromString(sortDirection.toUpperCase()); + PageRequest pageRequest = PageRequest.of(page, size, Sort.by(direction, sortBy)); + + Page usersPage = userApplicationService.getAllUsers(pageRequest); + PagedUserResponse response = toPagedResponse(usersPage); + + return ResponseEntity.ok(ApiResponse.success(response)); + } + + @GetMapping("/{userId}") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity> getUserById(@PathVariable String userId) { + // Fetch user by ID from the database + User user = userApplicationService.getUser(UserId.of(userId)); + + // Map to response DTO + UserProfileResponse response = UserProfileResponse.fromUser(user); + + return ResponseEntity.ok(ApiResponse.success(response)); + } + + private PagedUserResponse toPagedResponse(Page userPage) { + List items = userPage.getContent() + .stream() + .map(UserProfileResponse::fromUser) + .toList(); + + PageInfo pageInfo = new PageInfo( + userPage.getNumber(), + userPage.getSize(), + userPage.getTotalElements(), + userPage.getTotalPages() + ); + + return new PagedUserResponse(items, pageInfo); + } +} diff --git a/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationApplicationService.java b/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationApplicationService.java index 83bd6c1..e3aeeff 100644 --- a/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationApplicationService.java +++ b/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationApplicationService.java @@ -56,7 +56,7 @@ public AuthenticationResult authenticate(LoginCommand command) { email = "guest-" + System.currentTimeMillis() + "@commercify.com"; password = UUID.randomUUID().toString(); - CreateUserCommand createUserCommand = new CreateUserCommand(email, "Guest", "User", password, Set.of(UserRole.GUEST), null); + CreateUserCommand createUserCommand = new CreateUserCommand(email, "Guest", "User", password, Set.of(UserRole.ROLE_GUEST), null); userId = userApplicationService.createUser(createUserCommand); } @@ -64,6 +64,8 @@ public AuthenticationResult authenticate(LoginCommand command) { Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(email, password)); AuthenticatedUser authenticatedUser = (AuthenticatedUser) authentication.getPrincipal(); + User user = userApplicationService.getUser(userId); + // Generate tokens String accessToken = tokenService.generateAccessToken(authenticatedUser); String refreshToken = tokenService.generateRefreshToken(authenticatedUser); @@ -71,7 +73,7 @@ public AuthenticationResult authenticate(LoginCommand command) { // Publish domain event eventPublisher.publish(new UserAuthenticatedEvent(this, userId, email, command.isGuest())); - return new AuthenticationResult(accessToken, refreshToken, authenticatedUser); + return new AuthenticationResult(accessToken, refreshToken, authenticatedUser, user); } @Transactional(readOnly = true) @@ -93,7 +95,7 @@ public AuthenticationResult refreshToken(String refreshToken) { String newAccessToken = tokenService.generateAccessToken(authenticatedUser); String newRefreshToken = tokenService.generateRefreshToken(authenticatedUser); - return new AuthenticationResult(newAccessToken, newRefreshToken, authenticatedUser); + return new AuthenticationResult(newAccessToken, newRefreshToken, authenticatedUser, user); } public Optional extractTokenFromHeader(String authHeader) { diff --git a/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationResult.java b/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationResult.java index d7dc84f..6327ef6 100644 --- a/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationResult.java +++ b/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationResult.java @@ -1,10 +1,12 @@ package com.zenfulcode.commercify.auth.application.service; import com.zenfulcode.commercify.auth.domain.model.AuthenticatedUser; +import com.zenfulcode.commercify.user.domain.model.User; public record AuthenticationResult( String accessToken, String refreshToken, - AuthenticatedUser user + AuthenticatedUser user, + User userInfo ) { } \ No newline at end of file diff --git a/src/main/java/com/zenfulcode/commercify/auth/domain/model/UserRole.java b/src/main/java/com/zenfulcode/commercify/auth/domain/model/UserRole.java index b4ec167..8d11714 100644 --- a/src/main/java/com/zenfulcode/commercify/auth/domain/model/UserRole.java +++ b/src/main/java/com/zenfulcode/commercify/auth/domain/model/UserRole.java @@ -3,6 +3,5 @@ public enum UserRole { ROLE_GUEST, ROLE_USER, - ROLE_ADMIN, - ROLE_MANAGER + ROLE_ADMIN } \ No newline at end of file diff --git a/src/main/java/com/zenfulcode/commercify/user/application/dto/request/PagedUserResponse.java b/src/main/java/com/zenfulcode/commercify/user/application/dto/request/PagedUserResponse.java new file mode 100644 index 0000000..3c5c543 --- /dev/null +++ b/src/main/java/com/zenfulcode/commercify/user/application/dto/request/PagedUserResponse.java @@ -0,0 +1,12 @@ +package com.zenfulcode.commercify.user.application.dto.request; + +import com.zenfulcode.commercify.api.product.dto.response.PageInfo; +import com.zenfulcode.commercify.user.application.dto.response.UserProfileResponse; + +import java.util.List; + +public record PagedUserResponse( + List items, + PageInfo pageInfo +) { +} diff --git a/src/main/java/com/zenfulcode/commercify/user/application/dto/response/UserProfileResponse.java b/src/main/java/com/zenfulcode/commercify/user/application/dto/response/UserProfileResponse.java new file mode 100644 index 0000000..dfa3862 --- /dev/null +++ b/src/main/java/com/zenfulcode/commercify/user/application/dto/response/UserProfileResponse.java @@ -0,0 +1,45 @@ +package com.zenfulcode.commercify.user.application.dto.response; + +import com.zenfulcode.commercify.auth.domain.model.AuthenticatedUser; +import com.zenfulcode.commercify.user.domain.model.User; +import com.zenfulcode.commercify.user.domain.model.UserStatus; + +import java.util.List; + +public record UserProfileResponse( + String id, + String email, + String firstName, + String lastName, + String phoneNumber, + String status, + List roles +) { + public static UserProfileResponse fromUser(User user) { + return new UserProfileResponse( + user.getId().toString(), + user.getEmail(), + user.getFirstName(), + user.getLastName(), + user.getPhoneNumber(), + user.getStatus().name(), + user.getRoles().stream() + .map(Enum::name) + .toList() + ); + } + + public static UserProfileResponse fromAuthenticatedUser(AuthenticatedUser user) { + return new UserProfileResponse( + user.getUserId().getId(), + user.getEmail(), + user.getUsername(), + "", + "", + UserStatus.ACTIVE.toString(), + user.getRoles().stream() + .map(Enum::name) + .toList() + ); + } +} diff --git a/src/main/java/com/zenfulcode/commercify/user/application/service/UserApplicationService.java b/src/main/java/com/zenfulcode/commercify/user/application/service/UserApplicationService.java index d10e9cb..10e6e77 100644 --- a/src/main/java/com/zenfulcode/commercify/user/application/service/UserApplicationService.java +++ b/src/main/java/com/zenfulcode/commercify/user/application/service/UserApplicationService.java @@ -47,7 +47,7 @@ public UserId createUser(CreateUserCommand command) { @Transactional public void registerUser(String firstName, String lastName, String email, String password, String phone) { - CreateUserCommand createUserCommand = new CreateUserCommand(email, firstName, lastName, password, Set.of(UserRole.USER), phone); + CreateUserCommand createUserCommand = new CreateUserCommand(email, firstName, lastName, password, Set.of(UserRole.ROLE_USER), phone); createUser(createUserCommand); } diff --git a/src/main/java/com/zenfulcode/commercify/user/domain/model/User.java b/src/main/java/com/zenfulcode/commercify/user/domain/model/User.java index f03bdcb..4b92c2c 100644 --- a/src/main/java/com/zenfulcode/commercify/user/domain/model/User.java +++ b/src/main/java/com/zenfulcode/commercify/user/domain/model/User.java @@ -85,7 +85,7 @@ public static User create( user.lastName = Objects.requireNonNull(lastName, "Last name is required"); user.password = Objects.requireNonNull(password, "Password is required"); user.status = UserStatus.PENDING; - user.roles = new HashSet<>(roles != null ? roles : Set.of(UserRole.USER)); + user.roles = new HashSet<>(roles != null ? roles : Set.of(UserRole.ROLE_USER)); user.phoneNumber = phoneNumber; // Register domain event diff --git a/src/main/java/com/zenfulcode/commercify/user/domain/model/UserRole.java b/src/main/java/com/zenfulcode/commercify/user/domain/model/UserRole.java index 7dbd5dd..c6b4f90 100644 --- a/src/main/java/com/zenfulcode/commercify/user/domain/model/UserRole.java +++ b/src/main/java/com/zenfulcode/commercify/user/domain/model/UserRole.java @@ -1,9 +1,7 @@ package com.zenfulcode.commercify.user.domain.model; public enum UserRole { - GUEST, // Guest user - USER, // Standard user - ADMIN, // Administrator - MANAGER, // Store manager - SUPPORT // Customer support + ROLE_GUEST, + ROLE_USER, + ROLE_ADMIN } diff --git a/src/main/java/com/zenfulcode/commercify/user/infrastructure/config/AdminUserLoader.java b/src/main/java/com/zenfulcode/commercify/user/infrastructure/config/AdminUserLoader.java index 0fac8b3..35fd175 100644 --- a/src/main/java/com/zenfulcode/commercify/user/infrastructure/config/AdminUserLoader.java +++ b/src/main/java/com/zenfulcode/commercify/user/infrastructure/config/AdminUserLoader.java @@ -36,7 +36,7 @@ public void createAdminUser() { "User", "Admin", adminPassword, - Set.of(UserRole.ADMIN), + Set.of(UserRole.ROLE_ADMIN), null ); From a1b655fa4192bc7e055559cfd6adea95f188d432 Mon Sep 17 00:00:00 2001 From: gkhaavik Date: Wed, 7 May 2025 22:17:26 +0200 Subject: [PATCH 3/8] add updatedAt field to OrderDetailsDTO and related response classes --- .../zenfulcode/commercify/api/order/OrderController.java | 1 + .../api/order/dto/response/OrderDetailsResponse.java | 3 ++- .../commercify/api/order/mapper/OrderDtoMapper.java | 3 ++- .../commercify/order/application/dto/OrderDetailsDTO.java | 6 ++++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/zenfulcode/commercify/api/order/OrderController.java b/src/main/java/com/zenfulcode/commercify/api/order/OrderController.java index 8ea7ffe..8c8be5a 100644 --- a/src/main/java/com/zenfulcode/commercify/api/order/OrderController.java +++ b/src/main/java/com/zenfulcode/commercify/api/order/OrderController.java @@ -58,6 +58,7 @@ public ResponseEntity> createOrder( return ResponseEntity.ok(ApiResponse.success(response)); } + // TODO: SUPER NOT SECURE AND NOT GOOD @GetMapping("/{orderId}") public ResponseEntity> getOrder( @PathVariable String orderId, diff --git a/src/main/java/com/zenfulcode/commercify/api/order/dto/response/OrderDetailsResponse.java b/src/main/java/com/zenfulcode/commercify/api/order/dto/response/OrderDetailsResponse.java index 0e53c6b..e27d8bb 100644 --- a/src/main/java/com/zenfulcode/commercify/api/order/dto/response/OrderDetailsResponse.java +++ b/src/main/java/com/zenfulcode/commercify/api/order/dto/response/OrderDetailsResponse.java @@ -14,6 +14,7 @@ public record OrderDetailsResponse( CustomerDetailsResponse customerDetails, AddressResponse shippingAddress, AddressResponse billingAddress, - Instant createdAt + Instant createdAt, + Instant updatedAt ) { } diff --git a/src/main/java/com/zenfulcode/commercify/api/order/mapper/OrderDtoMapper.java b/src/main/java/com/zenfulcode/commercify/api/order/mapper/OrderDtoMapper.java index c9dfedd..7fc1afe 100644 --- a/src/main/java/com/zenfulcode/commercify/api/order/mapper/OrderDtoMapper.java +++ b/src/main/java/com/zenfulcode/commercify/api/order/mapper/OrderDtoMapper.java @@ -86,7 +86,8 @@ public OrderDetailsResponse toResponse(OrderDetailsDTO dto) { toAddressResponse(dto.shippingAddress()), dto.billingAddress() != null ? toAddressResponse(dto.billingAddress()) : null, - dto.createdAt() + dto.createdAt(), + dto.updatedAt() ); } diff --git a/src/main/java/com/zenfulcode/commercify/order/application/dto/OrderDetailsDTO.java b/src/main/java/com/zenfulcode/commercify/order/application/dto/OrderDetailsDTO.java index 212e367..2158800 100644 --- a/src/main/java/com/zenfulcode/commercify/order/application/dto/OrderDetailsDTO.java +++ b/src/main/java/com/zenfulcode/commercify/order/application/dto/OrderDetailsDTO.java @@ -21,7 +21,8 @@ public record OrderDetailsDTO(OrderId id, CustomerDetails customerDetails, Address shippingAddress, Address billingAddress, - Instant createdAt) { + Instant createdAt, + Instant updatedAt) { public static OrderDetailsDTO fromOrder(Order order) { return new OrderDetailsDTO( order.getId(), @@ -35,7 +36,8 @@ public static OrderDetailsDTO fromOrder(Order order) { order.getOrderShippingInfo().toCustomerDetails(), order.getOrderShippingInfo().toShippingAddress(), order.getOrderShippingInfo().toBillingAddress(), - order.getCreatedAt() + order.getCreatedAt(), + order.getUpdatedAt() ); } } From fc11a6a697434137cdcd5865f5d5a865dbb8b89c Mon Sep 17 00:00:00 2001 From: gkhaavik Date: Wed, 7 May 2025 22:34:57 +0200 Subject: [PATCH 4/8] refactor user roles to remove ROLE_ prefix and clean up related code --- .../com/zenfulcode/commercify/api/auth/AuthController.java | 6 ------ .../service/AuthenticationApplicationService.java | 2 +- .../user/application/service/UserApplicationService.java | 2 +- .../com/zenfulcode/commercify/user/domain/model/User.java | 2 +- .../zenfulcode/commercify/user/domain/model/UserRole.java | 6 +++--- .../user/infrastructure/config/AdminUserLoader.java | 2 +- 6 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java b/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java index a17ad7a..897c890 100644 --- a/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java +++ b/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java @@ -76,13 +76,7 @@ public ResponseEntity> getCurrentUser(@RequestH @PostMapping("/signin") public ResponseEntity> login(@RequestBody LoginRequest request) { - - System.out.println("Login request: " + request); AuthenticationResult result = authService.authenticate(request.toCommand()); - - System.out.println("Authentication result: " + result); - - AuthResponse response = AuthResponse.from(result); return ResponseEntity.ok(ApiResponse.success(response)); } diff --git a/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationApplicationService.java b/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationApplicationService.java index e3aeeff..7635841 100644 --- a/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationApplicationService.java +++ b/src/main/java/com/zenfulcode/commercify/auth/application/service/AuthenticationApplicationService.java @@ -56,7 +56,7 @@ public AuthenticationResult authenticate(LoginCommand command) { email = "guest-" + System.currentTimeMillis() + "@commercify.com"; password = UUID.randomUUID().toString(); - CreateUserCommand createUserCommand = new CreateUserCommand(email, "Guest", "User", password, Set.of(UserRole.ROLE_GUEST), null); + CreateUserCommand createUserCommand = new CreateUserCommand(email, "Guest", "User", password, Set.of(UserRole.GUEST), null); userId = userApplicationService.createUser(createUserCommand); } diff --git a/src/main/java/com/zenfulcode/commercify/user/application/service/UserApplicationService.java b/src/main/java/com/zenfulcode/commercify/user/application/service/UserApplicationService.java index 10e6e77..d10e9cb 100644 --- a/src/main/java/com/zenfulcode/commercify/user/application/service/UserApplicationService.java +++ b/src/main/java/com/zenfulcode/commercify/user/application/service/UserApplicationService.java @@ -47,7 +47,7 @@ public UserId createUser(CreateUserCommand command) { @Transactional public void registerUser(String firstName, String lastName, String email, String password, String phone) { - CreateUserCommand createUserCommand = new CreateUserCommand(email, firstName, lastName, password, Set.of(UserRole.ROLE_USER), phone); + CreateUserCommand createUserCommand = new CreateUserCommand(email, firstName, lastName, password, Set.of(UserRole.USER), phone); createUser(createUserCommand); } diff --git a/src/main/java/com/zenfulcode/commercify/user/domain/model/User.java b/src/main/java/com/zenfulcode/commercify/user/domain/model/User.java index 4b92c2c..f03bdcb 100644 --- a/src/main/java/com/zenfulcode/commercify/user/domain/model/User.java +++ b/src/main/java/com/zenfulcode/commercify/user/domain/model/User.java @@ -85,7 +85,7 @@ public static User create( user.lastName = Objects.requireNonNull(lastName, "Last name is required"); user.password = Objects.requireNonNull(password, "Password is required"); user.status = UserStatus.PENDING; - user.roles = new HashSet<>(roles != null ? roles : Set.of(UserRole.ROLE_USER)); + user.roles = new HashSet<>(roles != null ? roles : Set.of(UserRole.USER)); user.phoneNumber = phoneNumber; // Register domain event diff --git a/src/main/java/com/zenfulcode/commercify/user/domain/model/UserRole.java b/src/main/java/com/zenfulcode/commercify/user/domain/model/UserRole.java index c6b4f90..de0f457 100644 --- a/src/main/java/com/zenfulcode/commercify/user/domain/model/UserRole.java +++ b/src/main/java/com/zenfulcode/commercify/user/domain/model/UserRole.java @@ -1,7 +1,7 @@ package com.zenfulcode.commercify.user.domain.model; public enum UserRole { - ROLE_GUEST, - ROLE_USER, - ROLE_ADMIN + GUEST, + USER, + ADMIN } diff --git a/src/main/java/com/zenfulcode/commercify/user/infrastructure/config/AdminUserLoader.java b/src/main/java/com/zenfulcode/commercify/user/infrastructure/config/AdminUserLoader.java index 35fd175..0fac8b3 100644 --- a/src/main/java/com/zenfulcode/commercify/user/infrastructure/config/AdminUserLoader.java +++ b/src/main/java/com/zenfulcode/commercify/user/infrastructure/config/AdminUserLoader.java @@ -36,7 +36,7 @@ public void createAdminUser() { "User", "Admin", adminPassword, - Set.of(UserRole.ROLE_ADMIN), + Set.of(UserRole.ADMIN), null ); From f6398ff64b5fe6b9ec4c0d9efccaed20465a0306 Mon Sep 17 00:00:00 2001 From: gkhaavik Date: Thu, 8 May 2025 22:02:32 +0200 Subject: [PATCH 5/8] remove debug print statement from getCurrentUser method in AuthController --- .../java/com/zenfulcode/commercify/api/auth/AuthController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java b/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java index 897c890..e6041a5 100644 --- a/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java +++ b/src/main/java/com/zenfulcode/commercify/api/auth/AuthController.java @@ -57,8 +57,6 @@ public ResponseEntity> validateSession(@RequestHea @GetMapping("/me") public ResponseEntity> getCurrentUser(@RequestHeader("Authorization") String authHeader) { - System.out.println("Authorization Header: " + authHeader); - // Extract token using a domain service method String token = authService.extractTokenFromHeader(authHeader).orElseThrow(() -> new InvalidAuthenticationException("Invalid authorization header")); From 29f7b6f0bc61ecce71e9173bbe4583ce10e09901 Mon Sep 17 00:00:00 2001 From: gkhaavik Date: Thu, 8 May 2025 22:11:46 +0200 Subject: [PATCH 6/8] add isActive field to ProductResponseMapper and ProductSummaryResponse --- .../api/product/dto/response/ProductSummaryResponse.java | 3 ++- .../commercify/api/product/mapper/ProductResponseMapper.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/zenfulcode/commercify/api/product/dto/response/ProductSummaryResponse.java b/src/main/java/com/zenfulcode/commercify/api/product/dto/response/ProductSummaryResponse.java index 9f5ed59..5728cd2 100644 --- a/src/main/java/com/zenfulcode/commercify/api/product/dto/response/ProductSummaryResponse.java +++ b/src/main/java/com/zenfulcode/commercify/api/product/dto/response/ProductSummaryResponse.java @@ -8,6 +8,7 @@ public record ProductSummaryResponse( String description, String imageUrl, Money price, - int stock + int stock, + boolean isActive ) { } diff --git a/src/main/java/com/zenfulcode/commercify/api/product/mapper/ProductResponseMapper.java b/src/main/java/com/zenfulcode/commercify/api/product/mapper/ProductResponseMapper.java index e4dd6a1..fd04153 100644 --- a/src/main/java/com/zenfulcode/commercify/api/product/mapper/ProductResponseMapper.java +++ b/src/main/java/com/zenfulcode/commercify/api/product/mapper/ProductResponseMapper.java @@ -41,7 +41,8 @@ private ProductSummaryResponse toSummaryResponse(Product product) { product.getDescription(), product.getImageUrl(), product.getPrice(), - product.getStock() + product.getStock(), + product.isActive() ); } From 734de50702458f9c9fba6e12c6ec55ee12ce1abf Mon Sep 17 00:00:00 2001 From: gkhaavik Date: Thu, 8 May 2025 22:42:01 +0200 Subject: [PATCH 7/8] add canCaptureOrder method to OrderApplicationService and OrderDomainService --- .../order/application/service/OrderApplicationService.java | 4 ++++ .../commercify/order/domain/service/OrderDomainService.java | 5 +++++ .../application/service/PaymentApplicationService.java | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/src/main/java/com/zenfulcode/commercify/order/application/service/OrderApplicationService.java b/src/main/java/com/zenfulcode/commercify/order/application/service/OrderApplicationService.java index 15852fb..4fe85fa 100644 --- a/src/main/java/com/zenfulcode/commercify/order/application/service/OrderApplicationService.java +++ b/src/main/java/com/zenfulcode/commercify/order/application/service/OrderApplicationService.java @@ -128,4 +128,8 @@ public BigDecimal calculateTotalRevenue(CalculateTotalRevenueQuery query) { public int countOrdersInPeriod(CountOrdersInPeriodQuery query) { return orderDomainService.countOrdersInPeriod(query.startDate(), query.endDate()); } + + public void canCaptureOrder(OrderId orderId) { + orderDomainService.canCaptureOrder(orderId); + } } \ No newline at end of file diff --git a/src/main/java/com/zenfulcode/commercify/order/domain/service/OrderDomainService.java b/src/main/java/com/zenfulcode/commercify/order/domain/service/OrderDomainService.java index f7c6e72..d94d84d 100644 --- a/src/main/java/com/zenfulcode/commercify/order/domain/service/OrderDomainService.java +++ b/src/main/java/com/zenfulcode/commercify/order/domain/service/OrderDomainService.java @@ -155,4 +155,9 @@ public int countOrdersInPeriod(LocalDate startDate, LocalDate endDate) { return orderRepository.countOrders(start, end); } + + public void canCaptureOrder(OrderId orderId) { + Order order = getOrderById(orderId); + validationService.validateStatusTransition(order, OrderStatus.COMPLETED); + } } diff --git a/src/main/java/com/zenfulcode/commercify/payment/application/service/PaymentApplicationService.java b/src/main/java/com/zenfulcode/commercify/payment/application/service/PaymentApplicationService.java index 97fb755..3d27680 100644 --- a/src/main/java/com/zenfulcode/commercify/payment/application/service/PaymentApplicationService.java +++ b/src/main/java/com/zenfulcode/commercify/payment/application/service/PaymentApplicationService.java @@ -1,5 +1,6 @@ package com.zenfulcode.commercify.payment.application.service; +import com.zenfulcode.commercify.order.application.service.OrderApplicationService; import com.zenfulcode.commercify.payment.application.command.CapturePaymentCommand; import com.zenfulcode.commercify.payment.application.command.InitiatePaymentCommand; import com.zenfulcode.commercify.payment.application.dto.CaptureAmount; @@ -31,6 +32,7 @@ public class PaymentApplicationService { private final PaymentProviderFactory providerFactory; private final DefaultDomainEventPublisher eventPublisher; private final WebhookHandler webhookHandler; + private final OrderApplicationService orderApplicationService; @Transactional public InitializedPayment initiatePayment(InitiatePaymentCommand command) { @@ -79,6 +81,8 @@ public void handlePaymentCallback(PaymentProvider provider, WebhookPayload paylo // TODO: Make sure the capture currency is the same as the payment currency @Transactional public CapturedPayment capturePayment(CapturePaymentCommand command) { + orderApplicationService.canCaptureOrder(command.orderId()); + Payment payment = paymentDomainService.getPaymentByOrderId(command.orderId()); Money captureAmount = command.captureAmount() == null ? payment.getAmount() : command.captureAmount(); From 1baf7bbb04842f270e19df8db219c56b782328ff Mon Sep 17 00:00:00 2001 From: gkhaavik Date: Thu, 8 May 2025 23:24:39 +0200 Subject: [PATCH 8/8] add imageUrl field to ProductUpdateSpec and UpdateProductRequest; validate image URL in ProductDomainService --- .../api/product/dto/request/UpdateProductRequest.java | 1 + .../commercify/api/product/mapper/ProductDtoMapper.java | 1 + .../product/domain/service/ProductDomainService.java | 9 +++++++++ .../product/domain/valueobject/ProductUpdateSpec.java | 5 +++++ 4 files changed, 16 insertions(+) diff --git a/src/main/java/com/zenfulcode/commercify/api/product/dto/request/UpdateProductRequest.java b/src/main/java/com/zenfulcode/commercify/api/product/dto/request/UpdateProductRequest.java index df32707..215ab32 100644 --- a/src/main/java/com/zenfulcode/commercify/api/product/dto/request/UpdateProductRequest.java +++ b/src/main/java/com/zenfulcode/commercify/api/product/dto/request/UpdateProductRequest.java @@ -5,6 +5,7 @@ public record UpdateProductRequest( String name, String description, + String imageUrl, Integer stock, Money price, Boolean active diff --git a/src/main/java/com/zenfulcode/commercify/api/product/mapper/ProductDtoMapper.java b/src/main/java/com/zenfulcode/commercify/api/product/mapper/ProductDtoMapper.java index 79abe53..ea71f02 100644 --- a/src/main/java/com/zenfulcode/commercify/api/product/mapper/ProductDtoMapper.java +++ b/src/main/java/com/zenfulcode/commercify/api/product/mapper/ProductDtoMapper.java @@ -48,6 +48,7 @@ public UpdateProductCommand toCommand(ProductId productId, UpdateProductRequest ProductUpdateSpec updateSpec = new ProductUpdateSpec( request.name(), request.description(), + request.imageUrl(), request.stock(), request.price(), request.active() diff --git a/src/main/java/com/zenfulcode/commercify/product/domain/service/ProductDomainService.java b/src/main/java/com/zenfulcode/commercify/product/domain/service/ProductDomainService.java index 653281c..aa0e015 100644 --- a/src/main/java/com/zenfulcode/commercify/product/domain/service/ProductDomainService.java +++ b/src/main/java/com/zenfulcode/commercify/product/domain/service/ProductDomainService.java @@ -186,6 +186,15 @@ public void updateProduct(Product product, ProductUpdateSpec updateSpec) { } } + if (updateSpec.hasImageUrlUpdate()) { + if (updateSpec.imageUrl() == null || updateSpec.imageUrl().isBlank()) { + violations.add("Image URL cannot be empty"); + } else { + // TODO: Validate image URL format + product.setImageUrl(updateSpec.imageUrl()); + } + } + // If there are any violations, throw an exception if (!violations.isEmpty()) { throw new ProductValidationException(violations); diff --git a/src/main/java/com/zenfulcode/commercify/product/domain/valueobject/ProductUpdateSpec.java b/src/main/java/com/zenfulcode/commercify/product/domain/valueobject/ProductUpdateSpec.java index a5bc011..45b6f28 100644 --- a/src/main/java/com/zenfulcode/commercify/product/domain/valueobject/ProductUpdateSpec.java +++ b/src/main/java/com/zenfulcode/commercify/product/domain/valueobject/ProductUpdateSpec.java @@ -5,6 +5,7 @@ public record ProductUpdateSpec( String name, String description, + String imageUrl, Integer stock, Money price, Boolean active @@ -28,4 +29,8 @@ public boolean hasPriceUpdate() { public boolean hasActiveUpdate() { return active != null; } + + public boolean hasImageUrlUpdate() { + return imageUrl != null; + } } \ No newline at end of file