feat: basic Recipe class and RecipeController with GET, POST, PUT, DELETE

This commit is contained in:
Oskar Rasieński 2025-11-25 17:46:15 +01:00
commit 3aa530ffde
7 changed files with 899 additions and 0 deletions

View file

@ -0,0 +1,130 @@
package server.api;
import commons.Recipe;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import server.database.RecipeRepository;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api")
public class RecipeController {
private final RecipeRepository recipeRepository; // JPA repository used in this controller
public RecipeController(RecipeRepository recipeRepository) {
this.recipeRepository = recipeRepository;
}
/**
* Mapping for <code>GET /recipe/{id}</code>
* <p>
* Gets a specific recipe by its unique id.
* </p>
* @param id id of the recipe
* @return The recipe if it exists in the repository; otherwise returns 404 Not Found status
*/
@GetMapping("/recipe/{id}")
public ResponseEntity<Recipe> getRecipe(@PathVariable Long id) {
if (!recipeRepository.existsById(id)) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(recipeRepository.findById(id).get());
}
/**
* Mapping for <code>GET /recipes(?limit=)</code>
* <p>
* If the limit parameter is unspecified, return all recipes in the repository.
* @param limit Integer limit of items you want to get
* @return The list of recipes
*/
@GetMapping("/recipes")
public ResponseEntity<List<Recipe>> getRecipes(@RequestParam Optional<Integer> limit) {
if (limit.isPresent()) {
return ResponseEntity.ok(
recipeRepository.findAll(
PageRequest.of(0, limit.get())
).toList());
}
return ResponseEntity.ok(recipeRepository.findAll());
}
/**
* Mapping for <code>POST /recipe/{id}</code>
* @param id The recipe id to replace
* @param recipe The new recipe to be replaced from the original
* @return The changed recipe; returns 400 Bad Request if the recipe does not exist
*/
@PostMapping("/recipe/{id}")
public ResponseEntity<Recipe> updateRecipe(@PathVariable Long id, @RequestBody Recipe recipe) {
if (!recipeRepository.existsById(id)) {
return ResponseEntity.badRequest().build();
}
// TODO: Send WS update to all subscribers with the updated recipe
return ResponseEntity.ok(recipeRepository.save(recipe));
}
/**
* Mapping for <code>PUT /recipe/new</code>
* <p>
* Inserts a new recipe into the repository
* </p>
* @param recipe The new recipe as a request body
* @return 200 OK with the recipe you added; or 400 Bad Request if the recipe already exists by name
*/
@PutMapping("/recipe/new")
public ResponseEntity<Recipe> createRecipe(@RequestBody Recipe recipe) {
// We initialize a new example recipe with the name of input recipe
// This is the only attribute we are concerned about making sure it's unique
Recipe example = new Recipe();
example.setName(recipe.getName());
/* Here we use very funny JPA magic repository.exists(Example<Recipe>)
We check if any recipe in the repository has the same name as the input
*/
if (recipeRepository.exists(Example.of(example))) {
return ResponseEntity.badRequest().build();
}
// TODO: Send WS update to all subscribers with the new recipe
return ResponseEntity.ok(recipeRepository.save(recipe));
}
/**
* Mapping for <code>DELETE /recipe/{id}</code>
* <p>
* Deletes a recipe identified by its <code>id</code>.
* </p>
* @param id The id of the recipe to be deleted.
* @return 200 OK with true; or 400 Bad Request if the recipe doesn't exist.
*/
@DeleteMapping("/recipe/{id}")
public ResponseEntity<Boolean> deleteRecipe(@PathVariable Long id) {
if (!recipeRepository.existsById(id)) {
return ResponseEntity.badRequest().build();
}
recipeRepository.deleteById(id);
// TODO: Send WS update to propagate deletion
return ResponseEntity.ok(true);
}
}

View file

@ -0,0 +1,22 @@
/*
* Copyright 2021 Delft University of Technology
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server.database;
import org.springframework.data.jpa.repository.JpaRepository;
import commons.Recipe;
public interface RecipeRepository extends JpaRepository<Recipe, Long> {}

View file

@ -0,0 +1,18 @@
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# use one of these alternatives...
# ... purely in-memory, wiped on restart, but great for testing
spring.datasource.url=jdbc:h2:mem:testdb
# ... persisted on disk (in project directory)
#spring.datasource.url=jdbc:h2:file:./h2-database
# enable DB view on http://localhost:8080/h2-console
spring.h2.console.enabled=true
# strategy for table (re-)generation
spring.jpa.hibernate.ddl-auto=update
# show auto-generated SQL commands
#spring.jpa.hibernate.show_sql=true