-
Notifications
You must be signed in to change notification settings - Fork 0
Dev #9
Changes from all commits
da5cc25
459a77c
3d6136c
da92160
7ecf4f5
286a8dc
8af715d
ebfb84e
e47685a
d84622d
67a2f13
44862f0
70e55cc
d08e500
0715511
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package com.techtorque.project_service.client; | ||
|
|
||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.http.*; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.client.RestTemplate; | ||
| import org.springframework.web.client.HttpClientErrorException; | ||
|
|
||
| /** | ||
| * Client for inter-service communication with Appointment Service | ||
| */ | ||
| @Component | ||
| @Slf4j | ||
| public class AppointmentClient { | ||
|
|
||
| private final RestTemplate restTemplate; | ||
| private final String appointmentServiceUrl; | ||
|
|
||
| public AppointmentClient( | ||
| RestTemplate restTemplate, | ||
| @Value("${services.appointment.url:http://localhost:8083}") String appointmentServiceUrl) { | ||
| this.restTemplate = restTemplate; | ||
| this.appointmentServiceUrl = appointmentServiceUrl; | ||
| } | ||
|
|
||
| /** | ||
| * Cancel an appointment (used when project is rejected) | ||
| */ | ||
| public void cancelAppointment(String appointmentId, String adminId) { | ||
| try { | ||
| String url = appointmentServiceUrl + "/api/appointments/" + appointmentId; | ||
|
|
||
| HttpHeaders headers = new HttpHeaders(); | ||
| headers.set("X-User-Subject", adminId); | ||
| headers.set("X-User-Roles", "ADMIN"); | ||
|
|
||
| HttpEntity<Void> request = new HttpEntity<>(headers); | ||
|
|
||
| restTemplate.exchange(url, HttpMethod.DELETE, request, Void.class); | ||
|
|
||
| log.info("Successfully cancelled appointment {} via Appointment Service", appointmentId); | ||
| } catch (HttpClientErrorException e) { | ||
| log.error("Failed to cancel appointment {}: {}", appointmentId, e.getMessage()); | ||
| // Don't throw - project rejection should still succeed even if appointment cancellation fails | ||
| } catch (Exception e) { | ||
| log.error("Error communicating with Appointment Service: {}", e.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Update appointment status to CONFIRMED (used when project is approved) | ||
| */ | ||
| public void confirmAppointment(String appointmentId, String adminId) { | ||
| try { | ||
| String url = appointmentServiceUrl + "/api/appointments/" + appointmentId + "/status"; | ||
|
|
||
| HttpHeaders headers = new HttpHeaders(); | ||
| headers.set("X-User-Subject", adminId); | ||
| headers.set("X-User-Roles", "ADMIN"); | ||
| headers.setContentType(MediaType.APPLICATION_JSON); | ||
|
|
||
| String body = "{\"newStatus\":\"CONFIRMED\"}"; | ||
| HttpEntity<String> request = new HttpEntity<>(body, headers); | ||
|
|
||
| restTemplate.exchange(url, HttpMethod.PATCH, request, String.class); | ||
|
|
||
| log.info("Successfully confirmed appointment {} via Appointment Service", appointmentId); | ||
| } catch (HttpClientErrorException e) { | ||
| log.error("Failed to confirm appointment {}: {}", appointmentId, e.getMessage()); | ||
| // Don't throw - project approval should still succeed even if appointment confirmation fails | ||
| } catch (Exception e) { | ||
| log.error("Error communicating with Appointment Service: {}", e.getMessage()); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package com.techtorque.project_service.client; | ||
|
|
||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.http.*; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.client.RestTemplate; | ||
| import org.springframework.web.client.HttpClientErrorException; | ||
|
|
||
| /** | ||
| * Client for inter-service communication with Notification Service | ||
| */ | ||
| @Component | ||
| @Slf4j | ||
| public class NotificationClient { | ||
|
|
||
| private final RestTemplate restTemplate; | ||
| private final String notificationServiceUrl; | ||
|
|
||
| public NotificationClient( | ||
| RestTemplate restTemplate, | ||
| @Value("${services.notification.url:http://localhost:8088}") String notificationServiceUrl) { | ||
| this.restTemplate = restTemplate; | ||
| this.notificationServiceUrl = notificationServiceUrl; | ||
| } | ||
|
|
||
| /** | ||
| * Send project notification to user | ||
| */ | ||
| public void sendProjectNotification(String userId, String type, String title, String message, String projectId) { | ||
| try { | ||
| String url = notificationServiceUrl + "/api/notifications/project"; | ||
|
|
||
| HttpHeaders headers = new HttpHeaders(); | ||
| headers.setContentType(MediaType.APPLICATION_JSON); | ||
|
|
||
| String body = String.format( | ||
| "{\"userId\":\"%s\",\"type\":\"%s\",\"title\":\"%s\",\"message\":\"%s\",\"referenceId\":\"%s\",\"referenceType\":\"PROJECT\"}", | ||
| userId, type, title, message, projectId); | ||
|
|
||
| HttpEntity<String> request = new HttpEntity<>(body, headers); | ||
|
|
||
| restTemplate.postForEntity(url, request, String.class); | ||
|
|
||
| log.info("Successfully sent project notification to user {}", userId); | ||
| } catch (HttpClientErrorException e) { | ||
| log.error("Failed to send notification to user {}: {}", userId, e.getMessage()); | ||
| // Don't throw - project operations should still succeed even if notification fails | ||
| } catch (Exception e) { | ||
| log.error("Error communicating with Notification Service: {}", e.getMessage()); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.techtorque.project_service.config; | ||
|
|
||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.web.client.RestTemplate; | ||
|
|
||
| @Configuration | ||
| public class RestTemplateConfig { | ||
|
|
||
| @Bean | ||
| public RestTemplate restTemplate() { | ||
| return new RestTemplate(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |
| import lombok.NoArgsConstructor; | ||
|
|
||
| import java.math.BigDecimal; | ||
| import java.time.LocalDate; | ||
|
||
|
|
||
| @Data | ||
| @Builder | ||
|
|
@@ -19,9 +20,14 @@ public class ProjectRequestDto { | |
| @NotBlank(message = "Vehicle ID is required") | ||
| private String vehicleId; | ||
|
|
||
| @NotBlank(message = "Project type is required") | ||
| private String projectType; | ||
|
|
||
| @NotBlank(message = "Description is required") | ||
| @Size(min = 10, max = 2000, message = "Description must be between 10 and 2000 characters") | ||
| private String description; | ||
|
|
||
| private String desiredCompletionDate; | ||
|
|
||
| private BigDecimal budget; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
| import org.hibernate.annotations.CreationTimestamp; | ||
| import org.hibernate.annotations.UpdateTimestamp; | ||
| import java.math.BigDecimal; | ||
| import java.time.LocalDate; | ||
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| @Entity | ||
|
|
@@ -24,10 +25,17 @@ public class Project { | |
| @Column(nullable = false) | ||
| private String vehicleId; | ||
|
|
||
| private String appointmentId; // Link to appointment if project was created from appointment booking | ||
|
|
||
| @Column(nullable = false) | ||
| private String projectType; | ||
|
|
||
| @Lob | ||
| @Column(nullable = false) | ||
| private String description; | ||
|
|
||
| private String desiredCompletionDate; | ||
|
|
||
| private BigDecimal budget; // Use BigDecimal for currency | ||
|
|
||
| @Enumerated(EnumType.STRING) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The URL pattern is inconsistent with the approve endpoint. The approve endpoint uses '/{projectId}/approve' (line 149), but this reject endpoint uses '/{projectId}/admin/reject'. For consistency and better API design, consider using '/{projectId}/reject' since the @PreAuthorize annotation already restricts access to admins. Note there's already a customer reject endpoint at '/{projectId}/reject' for quote rejection (line 110), so you may need to use a different pattern like '/{projectId}/admin/approve' and '/{projectId}/admin/reject' for both admin operations.