diff --git a/commons/src/main/java/commons/ws/Topics.java b/commons/src/main/java/commons/ws/Topics.java new file mode 100644 index 0000000..9eb8d9f --- /dev/null +++ b/commons/src/main/java/commons/ws/Topics.java @@ -0,0 +1,5 @@ +package commons.ws; + +public class Topics { + public static final String RECIPES = "/subscribe/recipe"; +} diff --git a/commons/src/main/java/commons/ws/messages/CreateRecipeMessage.java b/commons/src/main/java/commons/ws/messages/CreateRecipeMessage.java new file mode 100644 index 0000000..b036c1f --- /dev/null +++ b/commons/src/main/java/commons/ws/messages/CreateRecipeMessage.java @@ -0,0 +1,30 @@ +package commons.ws.messages; + +import commons.Recipe; + +/** + * Message sent when a new recipe is created. + * + * @see commons.ws.messages.Message.Type#RECIPE_CREATE + */ +public class CreateRecipeMessage implements Message { + private Recipe recipe; + + public CreateRecipeMessage(Recipe recipe) { + this.recipe = recipe; + } + + @Override + public Type getType() { + return Type.RECIPE_CREATE; + } + + /** + * Get the created recipe. + * + * @return The created recipe. + */ + public Recipe getRecipe() { + return recipe; + } +} diff --git a/commons/src/main/java/commons/ws/messages/DeleteRecipeMessage.java b/commons/src/main/java/commons/ws/messages/DeleteRecipeMessage.java new file mode 100644 index 0000000..1802525 --- /dev/null +++ b/commons/src/main/java/commons/ws/messages/DeleteRecipeMessage.java @@ -0,0 +1,28 @@ +package commons.ws.messages; + +/** + * Message sent when a recipe is deleted. + * + * @see commons.ws.messages.Message.Type#RECIPE_DELETE + */ +public class DeleteRecipeMessage implements Message { + private Long recipeId; + + public DeleteRecipeMessage(Long recipeId) { + this.recipeId = recipeId; + } + + @Override + public Type getType() { + return Type.RECIPE_DELETE; + } + + /** + * Get the ID of the deleted recipe. + * + * @return The ID of the deleted recipe. + */ + public Long getRecipeId() { + return recipeId; + } +} diff --git a/commons/src/main/java/commons/ws/messages/Message.java b/commons/src/main/java/commons/ws/messages/Message.java new file mode 100644 index 0000000..681474f --- /dev/null +++ b/commons/src/main/java/commons/ws/messages/Message.java @@ -0,0 +1,44 @@ +package commons.ws.messages; + +public interface Message { + public enum Type { + /** + * Message sent when a new recipe is created. + * + * @see commons.ws.messages.CreateRecipeMessage + */ + RECIPE_CREATE, + + /** + * Message sent when an existing recipe is updated. + * + * @see commons.ws.messages.UpdateRecipeMessage + */ + RECIPE_UPDATE, + + /** + * Message sent when a recipe is deleted. + * + * @see commons.ws.messages.DeleteRecipeMessage + */ + RECIPE_DELETE + } + + /** + * Get the type of the message. + * This can be used to match the message to the appropriate handler. + * + *
+ * Message msg = ...;
+ * switch (msg.getType()) {
+ * case RECIPE_CREATE -> handleCreate((CreateRecipeMessage) msg);
+ * case RECIPE_UPDATE -> handleUpdate((UpdateRecipeMessage) msg);
+ * default -> { /* handle other cases *\/ }
+ * }
+ *
+ *
+ * @return The type of the message.
+ */
+ public Type getType();
+}
diff --git a/commons/src/main/java/commons/ws/messages/UpdateRecipeMessage.java b/commons/src/main/java/commons/ws/messages/UpdateRecipeMessage.java
new file mode 100644
index 0000000..c623b75
--- /dev/null
+++ b/commons/src/main/java/commons/ws/messages/UpdateRecipeMessage.java
@@ -0,0 +1,30 @@
+package commons.ws.messages;
+
+import commons.Recipe;
+
+/**
+ * Message sent when an existing recipe is updated.
+ *
+ * @see commons.ws.messages.Message.Type#RECIPE_UPDATE
+ */
+public class UpdateRecipeMessage implements Message {
+ private Recipe recipe;
+
+ public UpdateRecipeMessage(Recipe recipe) {
+ this.recipe = recipe;
+ }
+
+ @Override
+ public Type getType() {
+ return Type.RECIPE_UPDATE;
+ }
+
+ /**
+ * Get the updated recipe.
+ *
+ * @return The updated recipe.
+ */
+ public Recipe getRecipe() {
+ return recipe;
+ }
+}
diff --git a/server/src/main/java/server/api/RecipeController.java b/server/src/main/java/server/api/RecipeController.java
index a1d79fe..989f9f0 100644
--- a/server/src/main/java/server/api/RecipeController.java
+++ b/server/src/main/java/server/api/RecipeController.java
@@ -2,10 +2,15 @@ package server.api;
import commons.Recipe;
+import commons.ws.Topics;
+import commons.ws.messages.CreateRecipeMessage;
+import commons.ws.messages.DeleteRecipeMessage;
+import commons.ws.messages.UpdateRecipeMessage;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -25,9 +30,11 @@ import java.util.Optional;
@RequestMapping("/api")
public class RecipeController {
private final RecipeRepository recipeRepository; // JPA repository used in this controller
+ private final SimpMessagingTemplate messagingTemplate;
- public RecipeController(RecipeRepository recipeRepository) {
+ public RecipeController(RecipeRepository recipeRepository, SimpMessagingTemplate messagingTemplate) {
this.recipeRepository = recipeRepository;
+ this.messagingTemplate = messagingTemplate;
}
/**
@@ -61,6 +68,7 @@ public class RecipeController {
PageRequest.of(0, limit.get())
).toList());
}
+
return ResponseEntity.ok(recipeRepository.findAll());
}
@@ -76,9 +84,10 @@ public class RecipeController {
return ResponseEntity.badRequest().build();
}
- // TODO: Send WS update to all subscribers with the updated recipe
+ Recipe saved = recipeRepository.save(recipe);
+ messagingTemplate.convertAndSend(Topics.RECIPES, new UpdateRecipeMessage(saved));
- return ResponseEntity.ok(recipeRepository.save(recipe));
+ return ResponseEntity.ok(saved);
}
/**
@@ -104,9 +113,10 @@ public class RecipeController {
return ResponseEntity.badRequest().build();
}
- // TODO: Send WS update to all subscribers with the new recipe
+ Recipe saved = recipeRepository.save(recipe);
+ messagingTemplate.convertAndSend(Topics.RECIPES, new CreateRecipeMessage(saved));
- return ResponseEntity.ok(recipeRepository.save(recipe));
+ return ResponseEntity.ok(saved);
}
/**
@@ -124,7 +134,8 @@ public class RecipeController {
}
recipeRepository.deleteById(id);
- // TODO: Send WS update to propagate deletion
+ messagingTemplate.convertAndSend(Topics.RECIPES, new DeleteRecipeMessage(id));
+
return ResponseEntity.ok(true);
}
}
diff --git a/server/src/test/java/server/api/RecipeControllerTest.java b/server/src/test/java/server/api/RecipeControllerTest.java
index f1a766e..5b76a9f 100644
--- a/server/src/test/java/server/api/RecipeControllerTest.java
+++ b/server/src/test/java/server/api/RecipeControllerTest.java
@@ -9,6 +9,7 @@ import org.junit.jupiter.api.TestInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.http.HttpStatus;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.test.context.ActiveProfiles;
import server.database.RecipeRepository;
@@ -30,6 +31,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
// This config uses an in-memory database
@ActiveProfiles("mock-data-test")
public class RecipeControllerTest {
+
+ @Autowired
+ private SimpMessagingTemplate template;
+
private RecipeController controller;
private List