From f063a245b972c4bf15c91e0d8a6b98e886c52471 Mon Sep 17 00:00:00 2001 From: Vincent Guillet Date: Mon, 10 Nov 2025 16:29:55 +0100 Subject: [PATCH] feat: implement image management functionality; add ImageController, ImageService, ImageMapper, and related DTOs --- .../api/controller/app/ImageController.java | 63 +++++++++++++++++++ .../app/ProductImageController.java | 11 ++++ .../fr/gameovergne/api/dto/app/ImageDTO.java | 21 +++++++ .../api/mapper/app/ImageMapper.java | 27 ++++++++ .../fr/gameovergne/api/model/app/Image.java | 7 +-- .../fr/gameovergne/api/model/app/Product.java | 2 +- .../fr/gameovergne/api/model/user/User.java | 2 +- .../api/repository/app/ImageRepository.java | 11 ++++ .../api/service/app/ImageService.java | 60 ++++++++++++++++++ 9 files changed, 198 insertions(+), 6 deletions(-) create mode 100644 api/src/main/java/fr/gameovergne/api/controller/app/ImageController.java create mode 100644 api/src/main/java/fr/gameovergne/api/controller/app/ProductImageController.java create mode 100644 api/src/main/java/fr/gameovergne/api/dto/app/ImageDTO.java create mode 100644 api/src/main/java/fr/gameovergne/api/mapper/app/ImageMapper.java create mode 100644 api/src/main/java/fr/gameovergne/api/repository/app/ImageRepository.java create mode 100644 api/src/main/java/fr/gameovergne/api/service/app/ImageService.java diff --git a/api/src/main/java/fr/gameovergne/api/controller/app/ImageController.java b/api/src/main/java/fr/gameovergne/api/controller/app/ImageController.java new file mode 100644 index 0000000..988643e --- /dev/null +++ b/api/src/main/java/fr/gameovergne/api/controller/app/ImageController.java @@ -0,0 +1,63 @@ +package fr.gameovergne.api.controller.app; + +import fr.gameovergne.api.dto.app.ImageDTO; +import fr.gameovergne.api.mapper.app.ImageMapper; +import fr.gameovergne.api.model.app.Image; +import fr.gameovergne.api.service.app.ImageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/app/images") +public class ImageController { + + private final ImageService imageService; + private final ImageMapper imageMapper; + + @Autowired + public ImageController(ImageService imageService, ImageMapper imageMapper) { + this.imageService = imageService; + this.imageMapper = imageMapper; + } + + @GetMapping + public List getAllImages() { + return imageService.getAllImages() + .stream() + .map(imageMapper::toDto) + .toList(); + } + + @GetMapping("/{id}") + public ResponseEntity getImageById(@PathVariable Long id) { + return imageService.getImageById(id) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + @PostMapping + public void saveImage(@RequestBody ImageDTO imageDTO) { + imageService.saveImage(imageMapper.fromDto(imageDTO)); + } + + @PutMapping("/{id}") + public ResponseEntity updateImage(@PathVariable Long id, @RequestBody ImageDTO imageDTO) { + Image image = imageMapper.fromDto(imageDTO); + image.setId(id); + return imageService.getImageById(id) + .map(existingImage -> imageService.updateImage(image) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build())) + .orElse(ResponseEntity.notFound().build()); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteImageById(@PathVariable Long id) { + return imageService.deleteImageById(id) + .map(image -> ResponseEntity.ok().body(image)) + .orElse(ResponseEntity.notFound().build()); + } +} diff --git a/api/src/main/java/fr/gameovergne/api/controller/app/ProductImageController.java b/api/src/main/java/fr/gameovergne/api/controller/app/ProductImageController.java new file mode 100644 index 0000000..26d3450 --- /dev/null +++ b/api/src/main/java/fr/gameovergne/api/controller/app/ProductImageController.java @@ -0,0 +1,11 @@ +package fr.gameovergne.api.controller.app; + +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/app/products_images") +public class ProductImageController { + + + +} diff --git a/api/src/main/java/fr/gameovergne/api/dto/app/ImageDTO.java b/api/src/main/java/fr/gameovergne/api/dto/app/ImageDTO.java new file mode 100644 index 0000000..c49ceb0 --- /dev/null +++ b/api/src/main/java/fr/gameovergne/api/dto/app/ImageDTO.java @@ -0,0 +1,21 @@ +package fr.gameovergne.api.dto.app; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ImageDTO { + + @JsonProperty("id") + private Long id; + + @JsonProperty("name") + private String name; + + @JsonProperty("url") + private String url; +} diff --git a/api/src/main/java/fr/gameovergne/api/mapper/app/ImageMapper.java b/api/src/main/java/fr/gameovergne/api/mapper/app/ImageMapper.java new file mode 100644 index 0000000..06b0d3b --- /dev/null +++ b/api/src/main/java/fr/gameovergne/api/mapper/app/ImageMapper.java @@ -0,0 +1,27 @@ +package fr.gameovergne.api.mapper.app; + +import fr.gameovergne.api.dto.app.ImageDTO; +import fr.gameovergne.api.model.app.Image; +import org.springframework.stereotype.Component; + +@Component +public class ImageMapper { + + public Image fromDto(ImageDTO imageDTO) { + if (imageDTO == null) return null; + Image image = new Image(); + image.setId(imageDTO.getId()); + image.setName(imageDTO.getName()); + image.setUrl(imageDTO.getUrl()); + return image; + } + + public ImageDTO toDto(Image image) { + if (image == null) return null; + ImageDTO imageDTO = new ImageDTO(); + imageDTO.setId(image.getId()); + imageDTO.setName(image.getName()); + imageDTO.setUrl(image.getUrl()); + return imageDTO; + } +} diff --git a/api/src/main/java/fr/gameovergne/api/model/app/Image.java b/api/src/main/java/fr/gameovergne/api/model/app/Image.java index fb72e45..0809284 100644 --- a/api/src/main/java/fr/gameovergne/api/model/app/Image.java +++ b/api/src/main/java/fr/gameovergne/api/model/app/Image.java @@ -1,6 +1,5 @@ package fr.gameovergne.api.model.app; -import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonIdentityInfo; import com.fasterxml.jackson.annotation.ObjectIdGenerators; import fr.gameovergne.api.model.user.User; @@ -26,7 +25,7 @@ public class Image { @Column(length = 120, unique = true, nullable = false) @NotBlank - private String title; + private String name; @Column(length = 255, unique = true, nullable = false) @NotBlank @@ -34,7 +33,7 @@ public class Image { @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @JoinTable( - name = "user_images", + name = "users_images", joinColumns = @JoinColumn(name = "image_id"), inverseJoinColumns = @JoinColumn(name = "user_id") ) @@ -42,7 +41,7 @@ public class Image { @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @JoinTable( - name = "product_images", + name = "products_images", joinColumns = @JoinColumn(name = "image_id"), inverseJoinColumns = @JoinColumn(name = "product_id") ) diff --git a/api/src/main/java/fr/gameovergne/api/model/app/Product.java b/api/src/main/java/fr/gameovergne/api/model/app/Product.java index 3a2a073..8ef3201 100644 --- a/api/src/main/java/fr/gameovergne/api/model/app/Product.java +++ b/api/src/main/java/fr/gameovergne/api/model/app/Product.java @@ -63,7 +63,7 @@ public class Product { @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @JoinTable( - name = "product_images", + name = "products_images", joinColumns = @JoinColumn(name = "image_id"), inverseJoinColumns = @JoinColumn(name = "product_id") ) diff --git a/api/src/main/java/fr/gameovergne/api/model/user/User.java b/api/src/main/java/fr/gameovergne/api/model/user/User.java index 2b1ffa4..7bac067 100644 --- a/api/src/main/java/fr/gameovergne/api/model/user/User.java +++ b/api/src/main/java/fr/gameovergne/api/model/user/User.java @@ -51,7 +51,7 @@ public class User implements UserDetails { @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @JoinTable( - name = "user_images", + name = "users_images", joinColumns = @JoinColumn(name = "image_id"), inverseJoinColumns = @JoinColumn(name = "user_id") ) diff --git a/api/src/main/java/fr/gameovergne/api/repository/app/ImageRepository.java b/api/src/main/java/fr/gameovergne/api/repository/app/ImageRepository.java new file mode 100644 index 0000000..84ebc7d --- /dev/null +++ b/api/src/main/java/fr/gameovergne/api/repository/app/ImageRepository.java @@ -0,0 +1,11 @@ +package fr.gameovergne.api.repository.app; + +import fr.gameovergne.api.model.app.Image; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface ImageRepository extends JpaRepository { + Optional findByName(String name); + Optional findByUrl(String url); +} diff --git a/api/src/main/java/fr/gameovergne/api/service/app/ImageService.java b/api/src/main/java/fr/gameovergne/api/service/app/ImageService.java new file mode 100644 index 0000000..a6f3874 --- /dev/null +++ b/api/src/main/java/fr/gameovergne/api/service/app/ImageService.java @@ -0,0 +1,60 @@ +package fr.gameovergne.api.service.app; + +import fr.gameovergne.api.model.app.Image; +import fr.gameovergne.api.repository.app.ImageRepository; +import jakarta.transaction.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class ImageService { + + private final ImageRepository imageRepository; + + @Autowired + public ImageService(ImageRepository imageRepository) { + this.imageRepository = imageRepository; + } + + @Transactional + public void saveImage(Image image) { + if (image.getId() == null) { + imageRepository.save(image); + } + } + + public List getAllImages() { + return imageRepository.findAll(); + } + + public Optional getImageById(Long id) { + return imageRepository.findById(id); + } + + public Optional getImageByName(String name) { + return imageRepository.findByName(name); + } + + public Optional getImageByUrl(String url) { + return imageRepository.findByUrl(url); + } + + @Transactional + public Optional updateImage(Image image) { + return imageRepository.findById(image.getId()).map(existingImage -> { + existingImage.setName(image.getName()); + existingImage.setUrl(image.getUrl()); + return imageRepository.save(existingImage); + }); + } + + @Transactional + public Optional deleteImageById(Long id) { + Optional image = imageRepository.findById(id); + image.ifPresent(imageRepository::delete); + return image; + } +}