Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
Expand Down Expand Up @@ -77,6 +78,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http)
)
.authorizeHttpRequests(authorize ->
authorize
.requestMatchers(HttpMethod.OPTIONS, "/**")
.permitAll()
.requestMatchers("/**")
.authenticated()
.anyRequest()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,70 +136,6 @@ private String resolveUserEmail(java.util.UUID userUUID) {
return userEmail;
}

/**
* Sends an email notification to the user with the Gemini API response.
*
* @param userEmail The email address of the user.
* @param geminiResponse The response DTO from the Gemini API.
*/
private void sendEmailNotification(
String userEmail,
PlanBuilderRequestDto geminiResponse
) {
// Sanitize email: trim whitespace and remove newlines
String cleanEmail = userEmail == null
? ""
: userEmail.trim().replaceAll("[\r\n]+", "");
String mailUrl =
"http://151.61.228.91:8082/api/mail/" +
URLEncoder.encode(cleanEmail, StandardCharsets.UTF_8);
log.info(
"Sending email request to: {} with payload: {}",
mailUrl,
geminiResponse.toJson()
);

// Retrieve JWT from the current request context
String jwt = getCurrentJwtToken();
if (jwt == null || jwt.isEmpty()) {
log.error(
"JWT token not found in the current request context. Email will not be sent."
);
return;
}

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(mailUrl))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + jwt)
.POST(HttpRequest.BodyPublishers.ofString(geminiResponse.toJson()))
.build();

try {
HttpClient client = HttpClient.newHttpClient();
log.info("Sending email notification request...");
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
log.info(
"Email notification response status: {}, body: {}",
response.statusCode(),
response.body()
);
if (response.statusCode() != 200) {
log.error(
"Error sending email notification: {}",
response.body()
);
} else {
log.info("Email notification sent successfully.");
}
} catch (IOException | InterruptedException e) {
log.error("Exception sending email notification", e);
}
}

private String getCurrentJwtToken() {
// Retrieve the JWT from the current HTTP request header
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,12 @@ public List<ActivityDto> getActivitiesByTomatoId(Integer tomatoId) {
log.info("Mapped activities to ActivityDto list: {}", result);
return result;
}

public TomatoDto getTomato(Integer id) {
log.info("Fetching TomatoDto by ID: {}", id);
TomatoEntity tomatoEntity = tomatoesRepository.findById(id);
TomatoDto result = entityMapper.tomatoEntityToDto(tomatoEntity);
log.info("Converted TomatoEntity to TomatoDto: {}", result);
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,22 @@ public UsersControllers(
/**
* Returns a list of the top 100 users ordered by total hours.
*
* @return a list of UserDto representing the users with their total hours
* @return a list of UsersDto representing the users with their total hours
*/
public List<UserDto> getUsers() {
public List<UsersDto> getUsers() {
log.info("Fetching top 100 users by total hours");
List<Object[]> results = usersRepository.findTop100UsersByTotalHours();
List<UserDto> users = new ArrayList<>();
List<UsersDto> users = new ArrayList<>();
for (Object[] row : results) {
UserDto userDto = new UserDto();
userDto.setId(
UsersDto usersDto = new UsersDto();
usersDto.setId(
row[0] != null ? UUID.fromString(row[0].toString()) : null
);
userDto.setUsername(row[1] != null ? row[1].toString() : null);
userDto.setTotalHours(
usersDto.setUsername(row[1] != null ? row[1].toString() : null);
usersDto.setTotalHours(
row[2] != null ? ((Number) row[2]).doubleValue() : 0.0
);
users.add(userDto);
users.add(usersDto);
}
log.info("Returning {} users", users.size());
return users;
Expand Down Expand Up @@ -295,14 +295,14 @@ public List<AchievementDto> getUserAchievements(UUID uuid) {
.stream()
.filter(a -> "Studied for 5 hours".equals(a.getDescription()))
.findFirst();
boolean studiedCompleted = totalHours >= 5.0;
boolean studiedCompleted = totalHours >= 1.0;
AchievementEntity studiedEntity = studiedAchievement.orElseGet(() ->
new AchievementEntity(uuid, "Studied for 5 hours", studiedCompleted)
new AchievementEntity(uuid, "Studied for 1 hours", studiedCompleted)
);
studiedEntity.setCompleted(studiedCompleted);
achievementsRepository.save(studiedEntity);
log.info(
"Updated/created 'Studied for 5 hours' achievement for user {}: completed={}",
"Updated/created 'Studied for 1 hours' achievement for user {}: completed={}",
uuid,
studiedCompleted
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package com.alessandra_alessandro.ketchapp.models.dto;

import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.UUID;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UserDto {

private UUID id;
private String username;
private Double totalHours;
}
private String email;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.alessandra_alessandro.ketchapp.models.dto;

import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UsersDto {

private UUID id;
private String username;
private Double totalHours;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/tomatoes")
public class TomatoesRoutes {

private final TomatoesControllers tomatoesController;

@Autowired
Expand All @@ -26,45 +26,122 @@ public TomatoesRoutes(TomatoesControllers tomatoesController) {
}

@Operation(
summary = "Create a new tomato record",
description = "Creates a new tomato record.",
tags = {"Activities"}
summary = "Create a new tomato record",
description = "Creates a new tomato record.",
tags = { "Activities" }
)
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Successfully created tomato record",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = TomatoDto.class))),
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "Successfully created tomato record",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = TomatoDto.class)
)
),
@ApiResponse(responseCode = "400", description = "Bad request"),
@ApiResponse(responseCode = "401", description = "Unauthorized (authentication required)"),
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@ApiResponse(
responseCode = "401",
description = "Unauthorized (authentication required)"
),
@ApiResponse(
responseCode = "500",
description = "Internal server error"
),
}
)
@PostMapping
public ResponseEntity<TomatoDto> createTomato(@RequestBody TomatoDto tomatoDtoToCreate) {
public ResponseEntity<TomatoDto> createTomato(
@RequestBody TomatoDto tomatoDtoToCreate
) {
try {
TomatoDto createdTomato = tomatoesController.createTomato(tomatoDtoToCreate);
return ResponseEntity.status(HttpStatus.CREATED).body(createdTomato);
TomatoDto createdTomato = tomatoesController.createTomato(
tomatoDtoToCreate
);
return ResponseEntity.status(HttpStatus.CREATED).body(
createdTomato
);
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}

@Operation(
summary = "Get all activities for a tomato",
description = "Fetches all activities related to a specific tomato by its id.",
tags = {"Activities"}
summary = "Get a tomato",
description = "Fetches a specific tomato by its id.",
tags = { "Tomatoes" }
)
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "Successfully retrieved tomato",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = TomatoDto.class)
)
),
@ApiResponse(
responseCode = "401",
description = "Unauthorized (authentication required)"
),
@ApiResponse(
responseCode = "404",
description = "Tomato not found"
),
@ApiResponse(
responseCode = "500",
description = "Internal server error"
),
}
)
@GetMapping("/{id}")
public ResponseEntity<TomatoDto> getTomato(@PathVariable Integer id) {
TomatoDto response = tomatoesController.getTomato(id);
if (response != null) {
return ResponseEntity.ok(response);
} else {
return ResponseEntity.notFound().build();
}
}

@Operation(
summary = "Get all activities for a tomato",
description = "Fetches all activities related to a specific tomato by its id.",
tags = { "Activities" }
)
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "Successfully retrieved activities",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ActivityDto.class)
)
),
@ApiResponse(
responseCode = "401",
description = "Unauthorized (authentication required)"
),
@ApiResponse(
responseCode = "404",
description = "Tomato or activities not found"
),
@ApiResponse(
responseCode = "500",
description = "Internal server error"
),
}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved activities",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = ActivityDto.class))),
@ApiResponse(responseCode = "401", description = "Unauthorized (authentication required)"),
@ApiResponse(responseCode = "404", description = "Tomato or activities not found"),
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@GetMapping("/{id}/activities")
public ResponseEntity<List<ActivityDto>> getActivitiesByTomatoId(@PathVariable Integer id) {
public ResponseEntity<List<ActivityDto>> getActivitiesByTomatoId(
@PathVariable Integer id
) {
try {
List<ActivityDto> activities = tomatoesController.getActivitiesByTomatoId(id);
List<ActivityDto> activities =
tomatoesController.getActivitiesByTomatoId(id);
if (activities != null) {
return ResponseEntity.ok(activities);
} else {
Expand Down
Loading
Loading