User Controller working, refactored structure

This commit is contained in:
durn
2026-03-02 18:35:04 -07:00
parent 137dcbf948
commit af514d408d
227 changed files with 21125 additions and 604 deletions
@@ -1,13 +0,0 @@
package com.example.database;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface FavoriteRepo extends CrudRepository<Favorite, FavoriteId> {
List<Favorite> findByIdUserId(Integer userId); // all favorites for a user
List<Favorite> findByIdRecipeId(Integer recipeId); // all users who favorited a recipe
}
@@ -1,10 +0,0 @@
package com.example.database;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ImageRepo extends CrudRepository<Image, Integer> {
List<Image> findByRecipeId(Integer recipeId);
}
@@ -1,9 +0,0 @@
package com.example.database;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface IngredientRepo extends CrudRepository<Ingredient, Integer> {
Ingredient findByName(String name);
}
@@ -1,84 +0,0 @@
package com.example.database;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.persistence.Id;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Column;
import java.time.LocalDateTime;
@Entity
@Table(name = "recipes")
public class Recipe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
@Column(columnDefinition = "TEXT")
private String description;
private Integer prepTimeMinutes;
private Integer cookTimeMinutes;
private Integer servings;
@Column(name = "user_id", nullable = false)
private Integer userId;
private String status;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// Default constructor required by JPA
public Recipe() {}
// Convenience constructor
public Recipe(String title, String description, Integer prepTimeMinutes, Integer cookTimeMinutes,
Integer servings, Integer userId, String status) {
this.title = title;
this.description = description;
this.prepTimeMinutes = prepTimeMinutes;
this.cookTimeMinutes = cookTimeMinutes;
this.servings = servings;
this.userId = userId;
this.status = status;
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
// Getters and setters for all fields
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Integer getPrepTimeMinutes() { return prepTimeMinutes; }
public void setPrepTimeMinutes(Integer prepTimeMinutes) { this.prepTimeMinutes = prepTimeMinutes; }
public Integer getCookTimeMinutes() { return cookTimeMinutes; }
public void setCookTimeMinutes(Integer cookTimeMinutes) { this.cookTimeMinutes = cookTimeMinutes; }
public Integer getServings() { return servings; }
public void setServings(Integer servings) { this.servings = servings; }
public Integer getUserId() { return userId; }
public void setUserId(Integer userId) { this.userId = userId; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}
@@ -1,57 +0,0 @@
package com.example.database;
import jakarta.persistence.*;
import java.math.BigDecimal;
@Entity
@Table(name = "recipe_ingredient_junction",
uniqueConstraints = {@UniqueConstraint(columnNames = {"recipe_id", "ingredient_id"})})
public class RecipeIngredient {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; // surrogate PK
@ManyToOne
@JoinColumn(name = "recipe_id", nullable = false)
private Recipe recipe;
@ManyToOne
@JoinColumn(name = "ingredient_id", nullable = false)
private Ingredient ingredient;
private BigDecimal quantity;
private String unit;
private String notes;
public RecipeIngredient() {}
public RecipeIngredient(Recipe recipe, Ingredient ingredient, BigDecimal quantity, String unit, String notes) {
this.recipe = recipe;
this.ingredient = ingredient;
this.quantity = quantity;
this.unit = unit;
this.notes = notes;
}
// Getters and setters
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public Recipe getRecipe() { return recipe; }
public void setRecipe(Recipe recipe) { this.recipe = recipe; }
public Ingredient getIngredient() { return ingredient; }
public void setIngredient(Ingredient ingredient) { this.ingredient = ingredient; }
public BigDecimal getQuantity() { return quantity; }
public void setQuantity(BigDecimal quantity) { this.quantity = quantity; }
public String getUnit() { return unit; }
public void setUnit(String unit) { this.unit = unit; }
public String getNotes() { return notes; }
public void setNotes(String notes) { this.notes = notes; }
}
@@ -1,10 +0,0 @@
package com.example.database;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface RecipeIngredientRepo extends CrudRepository<RecipeIngredient, Integer> {
// Custom query: find all ingredients for a recipe
Iterable<RecipeIngredient> findByRecipe(Recipe recipe);
}
@@ -1,8 +0,0 @@
package com.example.database;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface RecipeRepo extends CrudRepository<Recipe, Integer> {
}
@@ -1,43 +0,0 @@
package com.example.database;
import jakarta.persistence.*;
@Entity
@Table(name = "steps")
public class Step {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "recipe_id", nullable = false)
private Integer recipeId;
@Column(name = "step_number", nullable = false)
private Integer stepNumber;
@Column(name = "instruction", nullable = false, columnDefinition = "TEXT")
private String instruction;
// No-arg constructor required by JPA
public Step() {}
public Step(Integer recipeId, Integer stepNumber, String instruction) {
this.recipeId = recipeId;
this.stepNumber = stepNumber;
this.instruction = instruction;
}
// Getters and setters
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public Integer getRecipeId() { return recipeId; }
public void setRecipeId(Integer recipeId) { this.recipeId = recipeId; }
public Integer getStepNumber() { return stepNumber; }
public void setStepNumber(Integer stepNumber) { this.stepNumber = stepNumber; }
public String getInstruction() { return instruction; }
public void setInstruction(String instruction) { this.instruction = instruction; }
}
@@ -1,11 +0,0 @@
package com.example.database;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface StepRepo extends CrudRepository<Step, Integer> {
List<Step> findByRecipeIdOrderByStepNumber(Integer recipeId); // fetch steps in order
}
@@ -1,12 +0,0 @@
package com.example.database;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface TagRepo extends CrudRepository<Tag, Integer> {
Optional<Tag> findByName(String name);
}
@@ -1,61 +0,0 @@
package com.example.database;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.persistence.Id;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Column;
import java.time.LocalDateTime;
@Entity
@Table(name = "users") // matches your MySQL table name
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto-increment id
private Integer id;
@Column(nullable = false, unique = true) // username cannot be null & must be unique
private String username;
private String role;
@Column(unique = true)
private String email;
private String hashedpassword;
@Column(name = "created_at")
private LocalDateTime createdAt;
// Constructors
public User() {} // default constructor required by JPA
public User(String username, String role, String email, String hashedpassword, LocalDateTime createdAt) {
this.username = username;
this.role = role;
this.email = email;
this.hashedpassword = hashedpassword;
this.createdAt = createdAt;
}
// Getters and Setters
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getHashedpassword() { return hashedpassword; }
public void setHashedpassword(String hashedpassword) { this.hashedpassword = hashedpassword; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}
@@ -1,50 +1,20 @@
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// Uses your CorsConfigurationSource bean from CorsConfig.java
.cors(Customizer.withDefaults())
// For now, disable CSRF so you can POST from a separate frontend easily.
// If you later use cookies/sessions, revisit CSRF.
.csrf(csrf -> csrf.disable())
// Auth rules
.authorizeHttpRequests(auth -> auth
// Allow health check + auth endpoints without login
.requestMatchers("/api/health").permitAll()
.requestMatchers("/api/auth/**").permitAll()
// Everything else requires authentication (you can loosen this later)
.anyRequest().authenticated()
)
// Temporary: enables basic auth popup in browser tools.
// Later youll likely switch to JWT or session login.
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.anyRequest().permitAll()
);
return http.build();
}
}
@@ -1,28 +1,5 @@
package com.example.demo.controller;
import com.example.demo.dto.LoginRequest;
import com.example.demo.dto.RegisterRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
// TEMP: Register endpoint (service logic added later)
@PostMapping("/register")
public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest request) {
// For now just return what was sent (test validation first)
return ResponseEntity.ok("User registered: " + request.getUsername());
}
// TEMP: Login endpoint
@PostMapping("/login")
public ResponseEntity<?> login(@Valid @RequestBody LoginRequest request) {
return ResponseEntity.ok("Login attempt for: " + request.getUsernameOrEmail());
}
}
@@ -0,0 +1,64 @@
package com.example.demo.controller;
import java.util.List;
import org.springframework.http.HttpStatus;
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.RestController;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
@RestController
@RequestMapping("/api/users")
public class UserController {
private UserService userService;
public UserController(UserService userService) {
super();
this.userService = userService;
}
//build create user REST API
@PostMapping
public ResponseEntity<User> saveUser(@RequestBody User user){
return new ResponseEntity<User>(userService.saveUser(user), HttpStatus.CREATED);
}
//build get all users REST API
@GetMapping
public List<User> getAllUsers(){
return userService.getAllUsers();
}
//build get user by id REST API
// http://localhost:8080/api/users/(id number goes here)
@GetMapping("{id}")
public ResponseEntity<User> getUserById(@PathVariable("id") Integer userId){
return new ResponseEntity<User>(userService.getUserById(userId), HttpStatus.OK);
}
//build update user REST API
// http://localhost:8080/api/users/(id number goes here)
@PutMapping("{id}")
public ResponseEntity<User> updateUser(@PathVariable("id") Integer userId, @RequestBody User user){
return new ResponseEntity<User>(userService.updateUser(user, userId), HttpStatus.OK);
}
//build delete user REST API
@DeleteMapping("{id}")
public ResponseEntity<String> deleteUser(@PathVariable("id") Integer userId){
userService.deleteUser(userId);
return new ResponseEntity<String>("User deleted succesfully!", HttpStatus.OK);
}
}
@@ -1,18 +1,5 @@
package com.example.demo.dto;
import jakarta.validation.constraints.NotBlank;
public class LoginRequest {
@NotBlank(message = "usernameOrEmail is required")
private String usernameOrEmail;
@NotBlank(message = "password is required")
private String password;
public String getUsernameOrEmail() { return usernameOrEmail; }
public void setUsernameOrEmail(String usernameOrEmail) { this.usernameOrEmail = usernameOrEmail; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
@@ -1,53 +1,5 @@
package com.example.demo.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import jakarta.validation.constraints.Size;
import java.util.List;
public class RecipeCreateRequest {
@NotBlank(message = "title is required")
@Size(max = 100, message = "title must be 100 characters or less")
private String title;
@Size(max = 1000, message = "description must be 1000 characters or less")
private String description;
@PositiveOrZero(message = "prepTimeMinutes must be 0 or greater")
private int prepTimeMinutes;
@PositiveOrZero(message = "cookTimeMinutes must be 0 or greater")
private int cookTimeMinutes;
@PositiveOrZero(message = "servings must be 0 or greater")
private int servings;
@NotNull(message = "ingredients list is required")
private List<IngredientDto> ingredients;
@NotNull(message = "steps list is required")
private List<StepDto> steps;
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public int getPrepTimeMinutes() { return prepTimeMinutes; }
public void setPrepTimeMinutes(int prepTimeMinutes) { this.prepTimeMinutes = prepTimeMinutes; }
public int getCookTimeMinutes() { return cookTimeMinutes; }
public void setCookTimeMinutes(int cookTimeMinutes) { this.cookTimeMinutes = cookTimeMinutes; }
public int getServings() { return servings; }
public void setServings(int servings) { this.servings = servings; }
public List<IngredientDto> getIngredients() { return ingredients; }
public void setIngredients(List<IngredientDto> ingredients) { this.ingredients = ingredients; }
public List<StepDto> getSteps() { return steps; }
public void setSteps(List<StepDto> steps) { this.steps = steps; }
}
@@ -1,29 +1,5 @@
package com.example.demo.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class RegisterRequest {
@NotBlank(message = "username is required")
@Size(min = 3, max = 30, message = "username must be 3-30 characters")
private String username;
@NotBlank(message = "email is required")
@Email(message = "email must be valid")
private String email;
@NotBlank(message = "password is required")
@Size(min = 8, max = 100, message = "password must be at least 8 characters")
private String password;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
@@ -1,4 +1,4 @@
package com.example.database;
package com.example.demo.entity;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@@ -1,4 +1,4 @@
package com.example.database;
package com.example.demo.entity;
import jakarta.persistence.*;
import java.io.Serializable;
@@ -1,4 +1,4 @@
package com.example.database;
package com.example.demo.entity;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@@ -1,4 +1,4 @@
package com.example.database;
package com.example.demo.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@@ -1,5 +1,84 @@
package com.example.demo.entity;
public class Recipe {
}
package com.example.demo.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.persistence.Id;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Column;
import java.time.LocalDateTime;
@Entity
@Table(name = "recipes")
public class Recipe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
@Column(columnDefinition = "TEXT")
private String description;
private Integer prepTimeMinutes;
private Integer cookTimeMinutes;
private Integer servings;
@Column(name = "user_id", nullable = false)
private Integer userId;
private String status;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// Default constructor required by JPA
public Recipe() {}
// Convenience constructor
public Recipe(String title, String description, Integer prepTimeMinutes, Integer cookTimeMinutes,
Integer servings, Integer userId, String status) {
this.title = title;
this.description = description;
this.prepTimeMinutes = prepTimeMinutes;
this.cookTimeMinutes = cookTimeMinutes;
this.servings = servings;
this.userId = userId;
this.status = status;
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
// Getters and setters for all fields
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Integer getPrepTimeMinutes() { return prepTimeMinutes; }
public void setPrepTimeMinutes(Integer prepTimeMinutes) { this.prepTimeMinutes = prepTimeMinutes; }
public Integer getCookTimeMinutes() { return cookTimeMinutes; }
public void setCookTimeMinutes(Integer cookTimeMinutes) { this.cookTimeMinutes = cookTimeMinutes; }
public Integer getServings() { return servings; }
public void setServings(Integer servings) { this.servings = servings; }
public Integer getUserId() { return userId; }
public void setUserId(Integer userId) { this.userId = userId; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}
@@ -1,5 +1,57 @@
package com.example.demo.entity;
public class RecipeIngredient {
}
package com.example.demo.entity;
import jakarta.persistence.*;
import java.math.BigDecimal;
@Entity
@Table(name = "recipe_ingredient_junction",
uniqueConstraints = {@UniqueConstraint(columnNames = {"recipe_id", "ingredient_id"})})
public class RecipeIngredient {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; // surrogate PK
@ManyToOne
@JoinColumn(name = "recipe_id", nullable = false)
private Recipe recipe;
@ManyToOne
@JoinColumn(name = "ingredient_id", nullable = false)
private Ingredient ingredient;
private BigDecimal quantity;
private String unit;
private String notes;
public RecipeIngredient() {}
public RecipeIngredient(Recipe recipe, Ingredient ingredient, BigDecimal quantity, String unit, String notes) {
this.recipe = recipe;
this.ingredient = ingredient;
this.quantity = quantity;
this.unit = unit;
this.notes = notes;
}
// Getters and setters
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public Recipe getRecipe() { return recipe; }
public void setRecipe(Recipe recipe) { this.recipe = recipe; }
public Ingredient getIngredient() { return ingredient; }
public void setIngredient(Ingredient ingredient) { this.ingredient = ingredient; }
public BigDecimal getQuantity() { return quantity; }
public void setQuantity(BigDecimal quantity) { this.quantity = quantity; }
public String getUnit() { return unit; }
public void setUnit(String unit) { this.unit = unit; }
public String getNotes() { return notes; }
public void setNotes(String notes) { this.notes = notes; }
}
@@ -1,4 +1,4 @@
package com.example.database;
package com.example.demo.entity;
import jakarta.persistence.*;
@@ -1,4 +1,4 @@
package com.example.database;
package com.example.demo.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
@@ -1,5 +1,43 @@
package com.example.demo.entity;
public class Step {
}
package com.example.demo.entity;
import jakarta.persistence.*;
@Entity
@Table(name = "steps")
public class Step {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "recipe_id", nullable = false)
private Integer recipeId;
@Column(name = "step_number", nullable = false)
private Integer stepNumber;
@Column(name = "instruction", nullable = false, columnDefinition = "TEXT")
private String instruction;
// No-arg constructor required by JPA
public Step() {}
public Step(Integer recipeId, Integer stepNumber, String instruction) {
this.recipeId = recipeId;
this.stepNumber = stepNumber;
this.instruction = instruction;
}
// Getters and setters
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public Integer getRecipeId() { return recipeId; }
public void setRecipeId(Integer recipeId) { this.recipeId = recipeId; }
public Integer getStepNumber() { return stepNumber; }
public void setStepNumber(Integer stepNumber) { this.stepNumber = stepNumber; }
public String getInstruction() { return instruction; }
public void setInstruction(String instruction) { this.instruction = instruction; }
}
@@ -1,4 +1,4 @@
package com.example.database;
package com.example.demo.entity;
import jakarta.persistence.*;
@@ -1,5 +1,61 @@
package com.example.demo.entity;
public class User {
}
package com.example.demo.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.persistence.Id;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Column;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(nullable = false, unique = true)
private String username;
private String role;
@Column(unique = true)
private String email;
private String hashedpassword;
@Column(name = "created_at")
private LocalDateTime createdAt;
// Constructors
public User() {} // default constructor required by JPA
public User(String username, String role, String email, String hashedpassword, LocalDateTime createdAt) {
this.username = username;
this.role = role;
this.email = email;
this.hashedpassword = hashedpassword;
this.createdAt = createdAt;
}
// Getters and Setters
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getHashedpassword() { return hashedpassword; }
public void setHashedpassword(String hashedpassword) { this.hashedpassword = hashedpassword; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}
@@ -1,8 +1,5 @@
package com.example.demo.exception;
@SuppressWarnings("serial")
public class BadRequestException extends RuntimeException {
public BadRequestException(String message) {
super(message);
}
}
public class BadRequestException {
}
@@ -1,25 +1,5 @@
package com.example.demo.exception;
import java.time.LocalDateTime;
public class ErrorResponse {
private LocalDateTime timestamp;
private int status;
private String error;
private String message;
private String path;
public ErrorResponse(LocalDateTime timestamp, int status, String error, String message, String path) {
this.timestamp = timestamp;
this.status = status;
this.error = error;
this.message = message;
this.path = path;
}
public LocalDateTime getTimestamp() { return timestamp; }
public int getStatus() { return status; }
public String getError() { return error; }
public String getMessage() { return message; }
public String getPath() { return path; }
}
}
@@ -1,57 +1,5 @@
package com.example.demo.exception;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.time.LocalDateTime;
import java.util.stream.Collectors;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(NotFoundException ex, HttpServletRequest request) {
return buildError(HttpStatus.NOT_FOUND, ex.getMessage(), request.getRequestURI());
}
@ExceptionHandler(BadRequestException.class)
public ResponseEntity<ErrorResponse> handleBadRequest(BadRequestException ex, HttpServletRequest request) {
return buildError(HttpStatus.BAD_REQUEST, ex.getMessage(), request.getRequestURI());
}
// Handles @Valid validation failures from DTOs
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex,
HttpServletRequest request) {
String msg = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(err -> err.getField() + ": " + err.getDefaultMessage())
.collect(Collectors.joining(", "));
return buildError(HttpStatus.BAD_REQUEST, msg, request.getRequestURI());
}
// Fallback for anything you didn't explicitly handle
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneric(Exception ex, HttpServletRequest request) {
// In production you'd avoid returning raw exception messages.
return buildError(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected error occurred", request.getRequestURI());
}
private ResponseEntity<ErrorResponse> buildError(HttpStatus status, String message, String path) {
ErrorResponse body = new ErrorResponse(
LocalDateTime.now(),
status.value(),
status.getReasonPhrase(),
message,
path
);
return ResponseEntity.status(status).body(body);
}
}
@@ -1,8 +1,33 @@
package com.example.demo.exception;
@SuppressWarnings("serial")
public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus (value = HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException{
private static final long serialVersionUID = 1l;
private String resourceName;
private String fieldName;
private Object fieldValue;
public NotFoundException(String resourceName, String fieldName, Object fieldValue) {
super(String.format("%s not found with %s : %s", resourceName, fieldName, fieldValue));
this.resourceName = resourceName;
this.fieldName = fieldName;
this.fieldValue = fieldValue;
}
public String getResourceName() {
return resourceName;
}
public String getFieldName() {
return fieldName;
}
public Object getFieldValue() {
return fieldValue;
}
}
@@ -0,0 +1,11 @@
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.Favorite;
import com.example.demo.entity.FavoriteId;
import java.util.List;
public interface FavoriteRepo extends JpaRepository<Favorite, FavoriteId> {
}
@@ -0,0 +1,11 @@
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.Image;
import java.util.List;
public interface ImageRepo extends JpaRepository<Image, Integer> {
}
@@ -0,0 +1,9 @@
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.Ingredient;
public interface IngredientRepo extends JpaRepository<Ingredient, Integer> {
}
@@ -0,0 +1,9 @@
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.RecipeIngredient;
public interface RecipeIngredientRepo extends JpaRepository<RecipeIngredient, Integer> {
// Custom query: find all ingredients for a recipe
}
@@ -0,0 +1,8 @@
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.Recipe;
public interface RecipeRepo extends JpaRepository<Recipe, Integer> {
}
@@ -1,5 +0,0 @@
package com.example.demo.repository;
public class RecipeRepository {
}
@@ -1,11 +1,12 @@
package com.example.database;
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.demo.entity.RecipeTag;
import com.example.demo.entity.RecipeTagId;
import java.util.List;
@Repository
public interface RecipeTagRepo extends JpaRepository<RecipeTag, RecipeTagId> {
}
@@ -0,0 +1,12 @@
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.Step;
import java.util.List;
public interface StepRepo extends JpaRepository<Step, Integer> {
}
@@ -0,0 +1,13 @@
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.Tag;
import java.util.Optional;
public interface TagRepo extends JpaRepository<Tag, Integer> {
}
@@ -1,7 +1,9 @@
package com.example.database;
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.User;
public interface UserRepo extends JpaRepository<User, Integer> {
User findByUsername(String username);
}
@@ -1,5 +0,0 @@
package com.example.demo.repository;
public class UserRepository {
}
@@ -0,0 +1,61 @@
package com.example.demo.service.Impl;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Service;
import com.example.demo.entity.User;
import com.example.demo.exception.NotFoundException;
import com.example.demo.repository.UserRepo;
import com.example.demo.service.UserService;
@Service
public class UserServiceImpl implements UserService{
private UserRepo userRepository;
public UserServiceImpl(UserRepo userRepository) {
super();
this.userRepository = userRepository;
}
@Override
public User saveUser(User user) {
return userRepository.save(user);
}
@Override
public List<User> getAllUsers() {
return userRepository.findAll();
}
@Override
public User getUserById(Integer Id) {
return userRepository.findById(Id).orElseThrow(() ->
new NotFoundException("User", "id", Id));
}
@Override
public User updateUser(User user, Integer Id) {
User existingUser = userRepository.findById(Id).orElseThrow(
() -> new NotFoundException("User", "id", Id));
existingUser.setUsername(user.getUsername());
existingUser.setEmail(user.getEmail());
userRepository.save(existingUser);
return existingUser;
}
@Override
public void deleteUser(Integer Id) {
userRepository.findById(Id).orElseThrow(
() -> new NotFoundException("User", "id", Id));
userRepository.deleteById(Id);
}
}
@@ -1,5 +1,5 @@
package com.example.demo.service;
public class RecipeService {
public interface RecipeService {
}
@@ -1,5 +1,13 @@
package com.example.demo.service;
public class UserService {
import java.util.List;
import com.example.demo.entity.User;
public interface UserService {
User saveUser(User user);
List<User> getAllUsers();
User getUserById(Integer Id);
User updateUser(User user, Integer Id);
void deleteUser(Integer Id);
}