add Platform and Brand DTOs, mappers, and controllers; update security configuration for CORS

This commit is contained in:
Vincent Guillet
2025-10-31 18:33:19 +01:00
parent 7531ea9453
commit 4b692806c4
19 changed files with 306 additions and 32 deletions

View File

@@ -4,9 +4,11 @@ import fr.gameovergne.api.service.security.JpaUserDetailsService;
import fr.gameovergne.api.service.security.filter.JwtAuthFilter; import fr.gameovergne.api.service.security.filter.JwtAuthFilter;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -17,8 +19,10 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@@ -36,35 +40,37 @@ public class SecurityConfig {
@Bean @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http http
.cors(Customizer.withDefaults())
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authz -> authz .authorizeHttpRequests(authz -> authz
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() // autoriser les preflight
.requestMatchers("/api/auth/**").permitAll() .requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/users/**").authenticated() .requestMatchers("/api/users/**").authenticated()
.requestMatchers("/api/brands/**").authenticated() .requestMatchers("/api/brands/**").permitAll()
.requestMatchers("/api/platforms/**").permitAll()
.anyRequest().permitAll() .anyRequest().permitAll()
) )
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider()) .authenticationProvider(authenticationProvider())
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.httpBasic(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable); .formLogin(AbstractHttpConfigurer::disable);
return http.build(); return http.build();
} }
@Bean @Bean
public WebMvcConfigurer corsConfigurer() { public CorsConfigurationSource corsConfigurationSource() {
return new WebMvcConfigurer() { CorsConfiguration config = new CorsConfiguration();
@Override config.setAllowedOrigins(Arrays.asList("http://localhost:4200", "http://127.0.0.1:4200"));
public void addCorsMappings(CorsRegistry registry) { config.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
registry.addMapping("/**") config.setAllowedHeaders(Arrays.asList("Authorization","Content-Type","Accept"));
.allowedOrigins("http://localhost:4200", "http://127.0.0.1:4200") config.setExposedHeaders(Arrays.asList("Authorization"));
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") config.setAllowCredentials(true);
.allowedHeaders("*")
.allowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
} source.registerCorsConfiguration("/**", config);
}; return source;
} }
@Bean @Bean

View File

@@ -1,8 +1,11 @@
package fr.gameovergne.api.controller.app; package fr.gameovergne.api.controller.app;
import fr.gameovergne.api.dto.app.BrandDTO;
import fr.gameovergne.api.mapper.app.BrandMapper;
import fr.gameovergne.api.model.app.Brand; import fr.gameovergne.api.model.app.Brand;
import fr.gameovergne.api.service.app.BrandService; import fr.gameovergne.api.service.app.BrandService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -13,10 +16,12 @@ import java.util.List;
public class BrandController { public class BrandController {
private final BrandService brandService; private final BrandService brandService;
private final BrandMapper brandMapper;
@Autowired @Autowired
public BrandController(BrandService brandService) { public BrandController(BrandService brandService, BrandMapper brandMapper) {
this.brandService = brandService; this.brandService = brandService;
this.brandMapper = brandMapper;
} }
@GetMapping @GetMapping
@@ -32,12 +37,14 @@ public class BrandController {
} }
@PostMapping @PostMapping
public void saveBrand(@RequestBody Brand brand) { public void saveBrand(@RequestBody BrandDTO brandDTO) {
brandService.saveBrand(brand); brandService.saveBrand(brandMapper.fromDto(brandDTO));
} }
@PutMapping("/{id}") @PutMapping("/{id}")
public ResponseEntity<Brand> updateBrand(@PathVariable Long id, @RequestBody Brand brand) { public ResponseEntity<Brand> updateBrand(@PathVariable Long id, @RequestBody BrandDTO brandDTO) {
Brand brand = brandMapper.fromDto(brandDTO);
brand.setId(id);
return brandService.getBrandById(id) return brandService.getBrandById(id)
.map(existingBrand -> brandService.updateBrand(brand) .map(existingBrand -> brandService.updateBrand(brand)
.map(ResponseEntity::ok) .map(ResponseEntity::ok)

View File

@@ -0,0 +1,61 @@
package fr.gameovergne.api.controller.app;
import fr.gameovergne.api.dto.app.PlatformDTO;
import fr.gameovergne.api.mapper.app.PlatformMapper;
import fr.gameovergne.api.model.app.Platform;
import fr.gameovergne.api.service.app.PlatformService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/platforms")
public class PlatformController {
private final PlatformService platformService;
private final PlatformMapper platformMapper;
public PlatformController(PlatformService platformService, PlatformMapper platformMapper) {
this.platformService = platformService;
this.platformMapper = platformMapper;
}
@GetMapping
public List<PlatformDTO> getAllPlatforms() {
return platformService.getAllPlatforms()
.stream()
.map(platformMapper::toDto)
.toList();
}
@GetMapping("/{id}")
public ResponseEntity<Platform> getPlatformById(@PathVariable Long id) {
return platformService.getPlatformById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public void savePlatform(@RequestBody PlatformDTO platformDTO) {
platformService.savePlatform(platformMapper.fromDto(platformDTO));
}
@PutMapping("/{id}")
public ResponseEntity<Platform> updatePlatform(@PathVariable Long id, @RequestBody PlatformDTO platformDTO) {
Platform platform = platformMapper.fromDto(platformDTO);
platform.setId(id);
return platformService.getPlatformById(id)
.map(existingPlatform -> platformService.updatePlatform(platform)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build()))
.orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Platform> deletePlatformById(@PathVariable Long id) {
return platformService.deletePlatformById(id)
.map(platform -> ResponseEntity.ok().body(platform))
.orElse(ResponseEntity.notFound().build());
}
}

View File

@@ -27,7 +27,6 @@ public class AuthController {
@PostMapping("/register") @PostMapping("/register")
public ResponseEntity<AuthResponse> register(@RequestBody UserDTO dto) { public ResponseEntity<AuthResponse> register(@RequestBody UserDTO dto) {
System.out.println("Registering: " + dto);
return authService.register(UserMapper.fromDto(dto)) return authService.register(UserMapper.fromDto(dto))
.map((ResponseEntity::ok)) .map((ResponseEntity::ok))
.orElse(ResponseEntity.badRequest().build()); .orElse(ResponseEntity.badRequest().build());
@@ -35,7 +34,6 @@ public class AuthController {
@PostMapping("/login") @PostMapping("/login")
public ResponseEntity<AuthResponse> authenticate(@RequestBody AuthRequest request, HttpServletResponse response) { public ResponseEntity<AuthResponse> authenticate(@RequestBody AuthRequest request, HttpServletResponse response) {
System.out.println("Authenticating: " + request);
return authService.authenticate(request) return authService.authenticate(request)
.map(authResponse -> createAuthResponse(authResponse, response)) .map(authResponse -> createAuthResponse(authResponse, response))
.orElse(ResponseEntity.badRequest().build()); .orElse(ResponseEntity.badRequest().build());

View File

@@ -0,0 +1,15 @@
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 BrandDTO {
@JsonProperty("name")
private String name;
}

View File

@@ -0,0 +1,18 @@
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 PlatformDTO {
@JsonProperty("name")
private String name;
@JsonProperty("brand")
private BrandDTO brandDTO;
}

View File

@@ -12,6 +12,5 @@ public class UserDTO {
private String lastName; private String lastName;
private String username; private String username;
private String email; private String email;
private String password; // Note: In a real application, avoid sending passwords in DTOs
private String role; private String role;
} }

View File

@@ -0,0 +1,23 @@
package fr.gameovergne.api.mapper.app;
import fr.gameovergne.api.dto.app.BrandDTO;
import fr.gameovergne.api.model.app.Brand;
import org.springframework.stereotype.Component;
@Component
public class BrandMapper {
public Brand fromDto(BrandDTO brandDTO) {
if (brandDTO == null) return null;
Brand brand = new Brand();
brand.setName(brandDTO.getName());
return brand;
}
public BrandDTO toDto(Brand brand) {
if (brand == null) return null;
BrandDTO brandDTO = new BrandDTO();
brandDTO.setName(brand.getName());
return brandDTO;
}
}

View File

@@ -0,0 +1,41 @@
package fr.gameovergne.api.mapper.app;
import fr.gameovergne.api.dto.app.BrandDTO;
import fr.gameovergne.api.dto.app.PlatformDTO;
import fr.gameovergne.api.model.app.Platform;
import fr.gameovergne.api.service.app.BrandService;
import org.springframework.stereotype.Component;
@Component
public class PlatformMapper {
private final BrandService brandService;
public PlatformMapper(BrandService brandService) {
this.brandService = brandService;
}
public Platform fromDto(PlatformDTO platformDTO) {
if (platformDTO == null) return null;
Platform platform = new Platform();
platform.setName(platformDTO.getName());
if (platformDTO.getBrandDTO() != null && platformDTO.getBrandDTO().getName() != null) {
platform.setBrand(this.brandService.getBrandByName(platformDTO.getBrandDTO().getName()).orElse(null));
} else {
platform.setBrand(null);
}
return platform;
}
public PlatformDTO toDto(Platform platform) {
if (platform == null) return null;
PlatformDTO platformDTO = new PlatformDTO();
platformDTO.setName(platform.getName());
if (platform.getBrand() != null) {
platformDTO.setBrandDTO(new BrandDTO(platform.getBrand().getName()));
} else {
platformDTO.setBrandDTO(null);
}
return platformDTO;
}
}

View File

@@ -12,7 +12,6 @@ public class UserMapper {
user.setLastName(userDTO.getLastName()); user.setLastName(userDTO.getLastName());
user.setUsername(userDTO.getUsername()); user.setUsername(userDTO.getUsername());
user.setEmail(userDTO.getEmail()); user.setEmail(userDTO.getEmail());
user.setPassword(userDTO.getPassword());
user.setRole(Role.valueOf(userDTO.getRole() != null ? userDTO.getRole() : "USER")); user.setRole(Role.valueOf(userDTO.getRole() != null ? userDTO.getRole() : "USER"));
return user; return user;
} }

View File

@@ -1,6 +1,8 @@
package fr.gameovergne.api.model.app; package fr.gameovergne.api.model.app;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@@ -13,6 +15,7 @@ import java.util.List;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@Table(name = "brands") @Table(name = "brands")
public class Brand { public class Brand {
@@ -25,6 +28,5 @@ public class Brand {
private String name; private String name;
@OneToMany(mappedBy = "brand", cascade = CascadeType.ALL, orphanRemoval = true) @OneToMany(mappedBy = "brand", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference
private List<Platform> platforms; private List<Platform> platforms;
} }

View File

@@ -1,6 +1,8 @@
package fr.gameovergne.api.model.app; package fr.gameovergne.api.model.app;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@@ -14,6 +16,7 @@ import java.util.List;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@Table(name = "categories") @Table(name = "categories")
public class Category { public class Category {
@@ -26,6 +29,5 @@ public class Category {
private String name; private String name;
@OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true) @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference
private List<Product> products; private List<Product> products;
} }

View File

@@ -1,6 +1,8 @@
package fr.gameovergne.api.model.app; package fr.gameovergne.api.model.app;
import com.fasterxml.jackson.annotation.JsonBackReference; 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; import fr.gameovergne.api.model.user.User;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
@@ -14,6 +16,7 @@ import java.util.List;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@Table(name = "images") @Table(name = "images")
public class Image { public class Image {
@@ -35,7 +38,6 @@ public class Image {
joinColumns = @JoinColumn(name = "image_id"), joinColumns = @JoinColumn(name = "image_id"),
inverseJoinColumns = @JoinColumn(name = "user_id") inverseJoinColumns = @JoinColumn(name = "user_id")
) )
@JsonBackReference
private List<User> users; private List<User> users;
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})

View File

@@ -1,6 +1,9 @@
package fr.gameovergne.api.model.app; package fr.gameovergne.api.model.app;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@@ -13,6 +16,7 @@ import java.util.List;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@Table(name = "platforms") @Table(name = "platforms")
public class Platform { public class Platform {
@@ -29,6 +33,5 @@ public class Platform {
private Brand brand; private Brand brand;
@OneToMany(mappedBy = "platform", cascade = CascadeType.ALL, orphanRemoval = true) @OneToMany(mappedBy = "platform", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference
private List<Product> products; private List<Product> products;
} }

View File

@@ -1,6 +1,8 @@
package fr.gameovergne.api.model.app; package fr.gameovergne.api.model.app;
import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
@@ -15,6 +17,7 @@ import java.util.List;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@Table(name = "products") @Table(name = "products")
public class Product { public class Product {
@@ -39,12 +42,10 @@ public class Product {
@ManyToOne @ManyToOne
@JoinColumn(name = "category_id") @JoinColumn(name = "category_id")
@JsonBackReference
private Category category; private Category category;
@ManyToOne @ManyToOne
@JoinColumn(name = "platform_id") @JoinColumn(name = "platform_id")
@JsonBackReference
private Platform platform; private Platform platform;
@NotNull @NotNull

View File

@@ -0,0 +1,11 @@
package fr.gameovergne.api.repository.app;
import fr.gameovergne.api.model.app.Platform;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface PlatformRepository extends JpaRepository<Platform, Long> {
Optional<Platform> findById(Long id);
Optional<Platform> findByName(String name);
}

View File

@@ -1,11 +1,13 @@
package fr.gameovergne.api.service.app; package fr.gameovergne.api.service.app;
import fr.gameovergne.api.model.app.Brand; import fr.gameovergne.api.model.app.Brand;
import fr.gameovergne.api.model.app.Platform;
import fr.gameovergne.api.repository.app.BrandRepository; import fr.gameovergne.api.repository.app.BrandRepository;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -42,7 +44,20 @@ public class BrandService {
public Optional<Brand> updateBrand(Brand brand) { public Optional<Brand> updateBrand(Brand brand) {
return brandRepository.findById(brand.getId()).map(existingBrand -> { return brandRepository.findById(brand.getId()).map(existingBrand -> {
existingBrand.setName(brand.getName()); existingBrand.setName(brand.getName());
existingBrand.setPlatforms(brand.getPlatforms());
List<Platform> newPlatforms = brand.getPlatforms();
if (newPlatforms != null) {
if (existingBrand.getPlatforms() == null) {
existingBrand.setPlatforms(new ArrayList<>());
} else {
existingBrand.getPlatforms().clear();
}
for (Platform p : newPlatforms) {
p.setBrand(existingBrand);
existingBrand.getPlatforms().add(p);
}
}
return brandRepository.save(existingBrand); return brandRepository.save(existingBrand);
}); });
} }

View File

@@ -0,0 +1,71 @@
package fr.gameovergne.api.service.app;
import fr.gameovergne.api.model.app.Platform;
import fr.gameovergne.api.model.app.Product;
import fr.gameovergne.api.repository.app.PlatformRepository;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
public class PlatformService {
private final PlatformRepository platformRepository;
@Autowired
public PlatformService(PlatformRepository platformRepository) {
this.platformRepository = platformRepository;
}
@Transactional
public void savePlatform(Platform platform) {
if (platform.getId() == null) {
platformRepository.save(platform);
}
}
public List<Platform> getAllPlatforms() {
return platformRepository.findAll();
}
public Optional<Platform> getPlatformById(Long id) {
return platformRepository.findById(id);
}
public Optional<Platform> getPlatformByName(String name) {
return platformRepository.findByName(name);
}
@Transactional
public Optional<Platform> updatePlatform(Platform platform) {
return platformRepository.findById(platform.getId()).map(existingPlatform -> {
existingPlatform.setName(platform.getName());
existingPlatform.setBrand(platform.getBrand());
List<Product> newProducts = platform.getProducts();
if (newProducts != null) {
if (existingPlatform.getProducts() == null) {
existingPlatform.setProducts(new ArrayList<>());
} else {
existingPlatform.getProducts().clear();
}
for (Product p : newProducts) {
p.setPlatform(existingPlatform);
existingPlatform.getProducts().add(p);
}
}
return platformRepository.save(existingPlatform);
});
}
@Transactional
public Optional<Platform> deletePlatformById(Long id) {
Optional<Platform> platform = platformRepository.findById(id);
platform.ifPresent(platformRepository::delete);
return platform;
}
}

View File

@@ -2,7 +2,7 @@ spring.application.name=api
server.port=3000 server.port=3000
spring.datasource.url=jdbc:mysql://192.168.20.112:3306/gameovergne_app?useSSL=false&allowPublicKeyRetrieval=true spring.datasource.url=jdbc:mysql://localhost:3306/gameovergne_app?useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=gameovergne spring.datasource.username=gameovergne
spring.datasource.password=gameovergne spring.datasource.password=gameovergne