Skip to content
This repository was archived by the owner on Nov 23, 2025. It is now read-only.

Commit d08e500

Browse files
authored
Merge pull request #8 from TechTorque-2025/randitha-superbranch
Randitha superbranch
2 parents 60bb6b6 + 70e55cc commit d08e500

13 files changed

Lines changed: 299 additions & 5 deletions

File tree

.github/workflows/buildtest.yaml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
name: Build and Test Project Service
22

33
on:
4-
push:
5-
branches:
6-
- '**'
74
pull_request:
85
branches:
9-
- '**'
6+
- main
7+
- dev
8+
- devOps
109

1110
jobs:
1211
build-test:
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.techtorque.project_service.client;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.beans.factory.annotation.Value;
5+
import org.springframework.http.*;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.web.client.RestTemplate;
8+
import org.springframework.web.client.HttpClientErrorException;
9+
10+
/**
11+
* Client for inter-service communication with Appointment Service
12+
*/
13+
@Component
14+
@Slf4j
15+
public class AppointmentClient {
16+
17+
private final RestTemplate restTemplate;
18+
private final String appointmentServiceUrl;
19+
20+
public AppointmentClient(
21+
RestTemplate restTemplate,
22+
@Value("${services.appointment.url:http://localhost:8083}") String appointmentServiceUrl) {
23+
this.restTemplate = restTemplate;
24+
this.appointmentServiceUrl = appointmentServiceUrl;
25+
}
26+
27+
/**
28+
* Cancel an appointment (used when project is rejected)
29+
*/
30+
public void cancelAppointment(String appointmentId, String adminId) {
31+
try {
32+
String url = appointmentServiceUrl + "/api/appointments/" + appointmentId;
33+
34+
HttpHeaders headers = new HttpHeaders();
35+
headers.set("X-User-Subject", adminId);
36+
headers.set("X-User-Roles", "ADMIN");
37+
38+
HttpEntity<Void> request = new HttpEntity<>(headers);
39+
40+
restTemplate.exchange(url, HttpMethod.DELETE, request, Void.class);
41+
42+
log.info("Successfully cancelled appointment {} via Appointment Service", appointmentId);
43+
} catch (HttpClientErrorException e) {
44+
log.error("Failed to cancel appointment {}: {}", appointmentId, e.getMessage());
45+
// Don't throw - project rejection should still succeed even if appointment cancellation fails
46+
} catch (Exception e) {
47+
log.error("Error communicating with Appointment Service: {}", e.getMessage());
48+
}
49+
}
50+
51+
/**
52+
* Update appointment status to CONFIRMED (used when project is approved)
53+
*/
54+
public void confirmAppointment(String appointmentId, String adminId) {
55+
try {
56+
String url = appointmentServiceUrl + "/api/appointments/" + appointmentId + "/status";
57+
58+
HttpHeaders headers = new HttpHeaders();
59+
headers.set("X-User-Subject", adminId);
60+
headers.set("X-User-Roles", "ADMIN");
61+
headers.setContentType(MediaType.APPLICATION_JSON);
62+
63+
String body = "{\"newStatus\":\"CONFIRMED\"}";
64+
HttpEntity<String> request = new HttpEntity<>(body, headers);
65+
66+
restTemplate.exchange(url, HttpMethod.PATCH, request, String.class);
67+
68+
log.info("Successfully confirmed appointment {} via Appointment Service", appointmentId);
69+
} catch (HttpClientErrorException e) {
70+
log.error("Failed to confirm appointment {}: {}", appointmentId, e.getMessage());
71+
// Don't throw - project approval should still succeed even if appointment confirmation fails
72+
} catch (Exception e) {
73+
log.error("Error communicating with Appointment Service: {}", e.getMessage());
74+
}
75+
}
76+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.techtorque.project_service.client;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.beans.factory.annotation.Value;
5+
import org.springframework.http.*;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.web.client.RestTemplate;
8+
import org.springframework.web.client.HttpClientErrorException;
9+
10+
/**
11+
* Client for inter-service communication with Notification Service
12+
*/
13+
@Component
14+
@Slf4j
15+
public class NotificationClient {
16+
17+
private final RestTemplate restTemplate;
18+
private final String notificationServiceUrl;
19+
20+
public NotificationClient(
21+
RestTemplate restTemplate,
22+
@Value("${services.notification.url:http://localhost:8088}") String notificationServiceUrl) {
23+
this.restTemplate = restTemplate;
24+
this.notificationServiceUrl = notificationServiceUrl;
25+
}
26+
27+
/**
28+
* Send project notification to user
29+
*/
30+
public void sendProjectNotification(String userId, String type, String title, String message, String projectId) {
31+
try {
32+
String url = notificationServiceUrl + "/api/notifications/project";
33+
34+
HttpHeaders headers = new HttpHeaders();
35+
headers.setContentType(MediaType.APPLICATION_JSON);
36+
37+
String body = String.format(
38+
"{\"userId\":\"%s\",\"type\":\"%s\",\"title\":\"%s\",\"message\":\"%s\",\"referenceId\":\"%s\",\"referenceType\":\"PROJECT\"}",
39+
userId, type, title, message, projectId);
40+
41+
HttpEntity<String> request = new HttpEntity<>(body, headers);
42+
43+
restTemplate.postForEntity(url, request, String.class);
44+
45+
log.info("Successfully sent project notification to user {}", userId);
46+
} catch (HttpClientErrorException e) {
47+
log.error("Failed to send notification to user {}: {}", userId, e.getMessage());
48+
// Don't throw - project operations should still succeed even if notification fails
49+
} catch (Exception e) {
50+
log.error("Error communicating with Notification Service: {}", e.getMessage());
51+
}
52+
}
53+
}

project-service/src/main/java/com/techtorque/project_service/config/DataSeeder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ private void seedProjects() {
111111
Project project1 = Project.builder()
112112
.customerId(CUSTOMER_1_ID)
113113
.vehicleId("VEH-001")
114+
.projectType("Performance")
114115
.description("Install custom exhaust system and performance tuning")
115116
.budget(new BigDecimal("5000.00"))
116117
.status(ProjectStatus.APPROVED)
@@ -121,6 +122,7 @@ private void seedProjects() {
121122
Project project2 = Project.builder()
122123
.customerId(CUSTOMER_2_ID)
123124
.vehicleId("VEH-002")
125+
.projectType("Interior")
124126
.description("Full interior leather upholstery replacement")
125127
.budget(new BigDecimal("3000.00"))
126128
.status(ProjectStatus.QUOTED)
@@ -131,6 +133,7 @@ private void seedProjects() {
131133
Project project3 = Project.builder()
132134
.customerId(CUSTOMER_1_ID)
133135
.vehicleId("VEH-001")
136+
.projectType("Body Work")
134137
.description("Custom body kit installation and paint job")
135138
.budget(new BigDecimal("8000.00"))
136139
.status(ProjectStatus.IN_PROGRESS)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.techtorque.project_service.config;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.web.client.RestTemplate;
6+
7+
@Configuration
8+
public class RestTemplateConfig {
9+
10+
@Bean
11+
public RestTemplate restTemplate() {
12+
return new RestTemplate();
13+
}
14+
}

project-service/src/main/java/com/techtorque/project_service/controller/ProjectController.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,42 @@ public ResponseEntity<ApiResponse> listAllProjects() {
145145
return ResponseEntity.ok(ApiResponse.success("All projects retrieved successfully", response));
146146
}
147147

148+
@Operation(summary = "Approve a custom project request (admin only)")
149+
@PostMapping("/{projectId}/approve")
150+
@PreAuthorize("hasRole('ADMIN')")
151+
public ResponseEntity<ApiResponse> approveProject(
152+
@PathVariable String projectId,
153+
@RequestHeader("X-User-Subject") String adminId) {
154+
155+
Project project = projectService.approveProject(projectId, adminId);
156+
ProjectResponseDto response = mapToResponseDto(project);
157+
158+
return ResponseEntity.ok(ApiResponse.success("Project approved successfully", response));
159+
}
160+
161+
@Operation(summary = "Reject a custom project request (admin only)")
162+
@PostMapping("/{projectId}/admin/reject")
163+
@PreAuthorize("hasRole('ADMIN')")
164+
public ResponseEntity<ApiResponse> rejectProject(
165+
@PathVariable String projectId,
166+
@RequestParam(required = false) String reason,
167+
@RequestHeader("X-User-Subject") String adminId) {
168+
169+
Project project = projectService.rejectProject(projectId, reason, adminId);
170+
ProjectResponseDto response = mapToResponseDto(project);
171+
172+
return ResponseEntity.ok(ApiResponse.success("Project rejected successfully", response));
173+
}
174+
148175
// Helper method to map Entity to DTO
149176
private ProjectResponseDto mapToResponseDto(Project project) {
150177
return ProjectResponseDto.builder()
151178
.id(project.getId())
152179
.customerId(project.getCustomerId())
153180
.vehicleId(project.getVehicleId())
181+
.projectType(project.getProjectType())
154182
.description(project.getDescription())
183+
.desiredCompletionDate(project.getDesiredCompletionDate())
155184
.budget(project.getBudget())
156185
.status(project.getStatus())
157186
.progress(project.getProgress())

project-service/src/main/java/com/techtorque/project_service/dto/request/ProjectRequestDto.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import lombok.NoArgsConstructor;
1010

1111
import java.math.BigDecimal;
12+
import java.time.LocalDate;
1213

1314
@Data
1415
@Builder
@@ -19,9 +20,14 @@ public class ProjectRequestDto {
1920
@NotBlank(message = "Vehicle ID is required")
2021
private String vehicleId;
2122

23+
@NotBlank(message = "Project type is required")
24+
private String projectType;
25+
2226
@NotBlank(message = "Description is required")
2327
@Size(min = 10, max = 2000, message = "Description must be between 10 and 2000 characters")
2428
private String description;
2529

30+
private String desiredCompletionDate;
31+
2632
private BigDecimal budget;
2733
}

project-service/src/main/java/com/techtorque/project_service/dto/response/ProjectResponseDto.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ public class ProjectResponseDto {
1717
private String id;
1818
private String customerId;
1919
private String vehicleId;
20+
private String projectType;
2021
private String description;
22+
private String desiredCompletionDate;
2123
private BigDecimal budget;
2224
private ProjectStatus status;
2325
private int progress;

project-service/src/main/java/com/techtorque/project_service/entity/Project.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.hibernate.annotations.CreationTimestamp;
66
import org.hibernate.annotations.UpdateTimestamp;
77
import java.math.BigDecimal;
8+
import java.time.LocalDate;
89
import java.time.LocalDateTime;
910

1011
@Entity
@@ -24,10 +25,17 @@ public class Project {
2425
@Column(nullable = false)
2526
private String vehicleId;
2627

28+
private String appointmentId; // Link to appointment if project was created from appointment booking
29+
30+
@Column(nullable = false)
31+
private String projectType;
32+
2733
@Lob
2834
@Column(nullable = false)
2935
private String description;
3036

37+
private String desiredCompletionDate;
38+
3139
private BigDecimal budget; // Use BigDecimal for currency
3240

3341
@Enumerated(EnumType.STRING)

project-service/src/main/java/com/techtorque/project_service/entity/ProjectStatus.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
public enum ProjectStatus {
44
REQUESTED,
5+
PENDING_ADMIN_REVIEW, // Custom project awaiting admin approval
56
QUOTED,
67
APPROVED,
78
IN_PROGRESS,

0 commit comments

Comments
 (0)