Final Modif

This commit is contained in:
MAIZY Amaury 2025-04-30 16:57:55 +02:00
parent d66be27cfe
commit 3ea04efcc6
24 changed files with 5124 additions and 43 deletions

4473
log/avis_dev_log Normal file

File diff suppressed because it is too large Load diff

49
pom.xml
View file

@ -81,22 +81,20 @@
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.6</version> <version>2.8.6</version>
</dependency> </dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
@ -109,7 +107,32 @@
</excludes> </excludes>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.6.3</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View file

@ -10,13 +10,7 @@ import java.time.LocalDate;
public class AvisApplication { public class AvisApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(AvisApplication.class, args); SpringApplication.run(AvisApplication.class, args);
Joueur joueur = Joueur.builder()
.pseudo("Joueur")
.email("amaury.maizy@soprasteria.com")
.dateDeNaissance(LocalDate.of(2000, 1, 1))
.build();
} }
} }

View file

@ -0,0 +1,61 @@
package fr.clelia.avis.aop;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect
@AllArgsConstructor
@Log4j2
@Component
public class LoggingAspect {
// Toutes les méthodes dans les packages énumérés
/*
@Pointcut("within(fr.clelia.avis.repository..*)"
+ " || within(fr.clelia.avis.service..*)"
+ " || within(fr.clelia.avis.initialisation..*)"
+ " || within(fr.clelia.avis.controller..*)"
+ " || within(fr.clelia.avis.controller.rest..*)")
*/
// Toutes les méthodes de la classe
// @Pointcut("within(fr.clelia.avis.controller.rest.JeuRestController)")
// Toutes les classes annotées @Service
@Pointcut("@within(org.springframework.stereotype.Service)")
// Toutes les méthodes annotées @Transactional
//@Pointcut("execution(@org.springframework.transaction.annotation.Transactional * *(..))")
// Toutes les méthodes annotées @Transactional dont le nom débute par i
//@Pointcut("execution(@org.springframework.transaction.annotation.Transactional * i*(..))")
//@Pointcut("execution(@org.springframework.transaction.annotation.Transactional java.util.List i*(..))")
public void avisPackagePointcut() {
}
@Around("avisPackagePointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
try {
Object result = joinPoint.proceed();
log.info("Invocation {} avec arguments {} : ", joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
System.out.println("Invocation : " + joinPoint.getSignature().getName() + "() argument[s] = "
+ Arrays.toString(joinPoint.getArgs())
+ " resultat = " + result);
return result;
} catch (IllegalArgumentException e) {
log.error("Exception levée : {} dans {}()", Arrays.toString(joinPoint.getArgs()), joinPoint.getSignature().getName());
System.err.println("Exception levée : " + e.getMessage());
throw e;
}
}
}

View file

@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.*; import lombok.*;
import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.DynamicUpdate;
@ -26,7 +27,9 @@ public class Editeur {
private Long id; private Long id;
@Column(length = 100, unique = true) @Column(length = 100, unique = true)
// Contrainte métier : le nom de l'éditeur ne peut pas être une chaîne vide
@NotBlank(message="Merci de préciser le nom de l'éditeur") @NotBlank(message="Merci de préciser le nom de l'éditeur")
@Size(min=2, message="Le nom de l'éditeur doit comporter au moins {min} caractères")
private String nom; private String nom;
private String logo; private String logo;

View file

@ -1,6 +1,7 @@
package fr.clelia.avis.business; package fr.clelia.avis.business;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.validation.constraints.Past;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
@ -13,6 +14,7 @@ import java.time.LocalDate;
@Data @Data
public class Joueur extends Utilisateur { public class Joueur extends Utilisateur {
@Past(message="Merci de préciser une date de naissance dans le passé")
private LocalDate dateDeNaissance; private LocalDate dateDeNaissance;
public Joueur(String pseudo, String email, String motDePasse, LocalDate dateDeNaissance) { public Joueur(String pseudo, String email, String motDePasse, LocalDate dateDeNaissance) {

View file

@ -0,0 +1,20 @@
package fr.clelia.avis.controller.rest;
import fr.clelia.avis.util.ReponseApi;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class AvisRestControllerAdvice {
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ReponseApi<String> traiterDonneesInvalides(ConstraintViolationException e){
//return new ReponseApi<>(String.valueOf(HttpStatus.BAD_REQUEST.value()), e.getMessage(), null);
return new ReponseApi<>(String.valueOf(HttpStatus.BAD_REQUEST.value()), e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).toList().toString(), null);
}
}

View file

@ -1,15 +1,21 @@
package fr.clelia.avis.controller.rest; package fr.clelia.avis.controller.rest;
import fr.clelia.avis.business.Editeur; import fr.clelia.avis.business.Editeur;
import fr.clelia.avis.dto.EditeurDto;
import fr.clelia.avis.exception.EditeurInexistantException; import fr.clelia.avis.exception.EditeurInexistantException;
import fr.clelia.avis.mapper.EditeurMapper;
import fr.clelia.avis.service.EditeurService; import fr.clelia.avis.service.EditeurService;
import fr.clelia.avis.util.ReponseApi; import fr.clelia.avis.util.ReponseApi;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import jakarta.validation.ConstraintViolationException;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List; import java.util.List;
@AllArgsConstructor @AllArgsConstructor
@ -18,6 +24,7 @@ import java.util.List;
public class EditeurRestController { public class EditeurRestController {
private final EditeurService editeurService; private final EditeurService editeurService;
private final EditeurMapper editeurMapper;
@GetMapping("") @GetMapping("")
@Operation(summary="Récupère tous les éditeurs") @Operation(summary="Récupère tous les éditeurs")
@ -25,25 +32,44 @@ public class EditeurRestController {
return editeurService.recupererEditeurs(); return editeurService.recupererEditeurs();
} }
// @GetMapping("/{id}")
// @Operation(summary="Récupère un éditeur par son id")
// public EditeurDto getEditeur(@PathVariable Long id) {
// return editeurMapper.toDto(editeurService.recupererEditeur(id));
// }
@GetMapping("/{id}") @GetMapping("/{id}")
@Operation(summary="Récupère un éditeur par son id") @Operation(summary="Récupère un éditeur par son id")
public Editeur getEditeur(@PathVariable Long id) { public ResponseEntity<EditeurDto> getEditeur(@PathVariable Long id) {
return editeurService.recupererEditeur(id); return ResponseEntity.ok(editeurMapper.toDto(editeurService.recupererEditeur(id)));
} }
@PostMapping("") @PostMapping("")
@ResponseStatus(HttpStatus.CREATED) @ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "Ajoute un nouvel éditeur") @Operation(summary = "Ajoute un nouvel éditeur")
public Editeur postEditeur(@RequestBody Editeur editeur) { // https://sonarsource.atlassian.net/browse/RSPEC-4684
return editeurService.ajouterEditeur(editeur); //public Editeur postEditeur(@RequestBody Editeur editeur) {
public ResponseEntity<EditeurDto> postEditeur(@RequestBody EditeurDto editeurDto) throws URISyntaxException {
//return editeurMapper.toDto(editeurService.ajouterEditeur(editeurMapper.toEntity(editeurDto)));
EditeurDto editeurDtoAjoute = editeurMapper.toDto(editeurService.ajouterEditeur(editeurMapper.toEntity(editeurDto)));
return ResponseEntity.created(new URI("/api/editeurs/" + editeurDtoAjoute.id()))
.body(editeurDtoAjoute);
} }
// TODO comprendre comment les plateformes sur le findAll // TODO comprendre comment les plateformes sur le findAll
@DeleteMapping("/{id}")
@Operation(summary = "Supprime un éditeur")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteEditeur(@PathVariable Long id) {
editeurService.supprimerEditeur(id);
}
@ExceptionHandler(EditeurInexistantException.class) @ExceptionHandler(EditeurInexistantException.class)
@ResponseStatus(HttpStatus.NOT_FOUND) @ResponseStatus(HttpStatus.NOT_FOUND)
public ReponseApi<String> traiterEditeurInexistantException(EditeurInexistantException e){ public ReponseApi<String> traiterEditeurInexistantException(EditeurInexistantException e){
//return e.getMessage(); //return e.getMessage();
return new ReponseApi<>(String.valueOf(HttpStatus.NOT_FOUND.value()), e.getMessage(), null); return new ReponseApi<>(String.valueOf(HttpStatus.NOT_FOUND.value()), e.getMessage(), null);
} }
} }

View file

@ -0,0 +1,15 @@
package fr.clelia.avis.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.io.Serializable;
import java.util.List;
/**
* DTO for {@link fr.clelia.avis.business.Editeur}
*/
public record EditeurDto(Long id,
@Size(message = "Le nom de l'éditeur doit comporter au moins {min} caractères", min = 2) @NotBlank(message = "Merci de préciser le nom de l'éditeur") String nom,
String logo, List<JeuDto> jeux) implements Serializable {
}

View file

@ -0,0 +1,12 @@
package fr.clelia.avis.dto;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.List;
/**
* DTO for {@link fr.clelia.avis.business.Jeu}
*/
public record JeuDto(Long id, String nom, EditeurDto editeur, LocalDate dateDeSortie,
boolean possedeImage, List<PlateformeDto> plateformeDtoList) implements Serializable {
}

View file

@ -0,0 +1,4 @@
package fr.clelia.avis.dto;
public record NbJoueursParAnneeDeNaissance(int annee, long nbJoueurs) {
}

View file

@ -0,0 +1,6 @@
package fr.clelia.avis.dto;
import java.util.List;
public record PlateformeDto(Long id, String nom, List<JeuDto> jeux) {
}

View file

@ -5,6 +5,7 @@ import fr.clelia.avis.repository.*;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Profile;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -16,6 +17,7 @@ import java.util.Random;
@Component // Spring va instancier cette classe @Component // Spring va instancier cette classe
// et placer cette instance dans son conteneur IoC // et placer cette instance dans son conteneur IoC
@AllArgsConstructor @AllArgsConstructor
@Profile({"dev", "prod"})
public class AjoutDonneesInitiales { public class AjoutDonneesInitiales {
// Dépendances // Dépendances
@ -46,6 +48,7 @@ public class AjoutDonneesInitiales {
.forEach(System.out::println); .forEach(System.out::println);
joueurRepository.findJoueursTrentenaires().forEach(System.out::println); joueurRepository.findJoueursTrentenaires().forEach(System.out::println);
jeuRepository.findJeuxByPlateforme(plateformeRepository.findAll().get(0)).forEach(System.out::println); jeuRepository.findJeuxByPlateforme(plateformeRepository.findAll().get(0)).forEach(System.out::println);
joueurRepository.findNbJoueursParAnneeDeNaissance().forEach(System.out::println);
} }
private void ajouterEditeurs() { private void ajouterEditeurs() {

View file

@ -0,0 +1,23 @@
package fr.clelia.avis.mapper;
import fr.clelia.avis.business.Editeur;
import fr.clelia.avis.dto.EditeurDto;
import org.mapstruct.*;
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = MappingConstants.ComponentModel.SPRING, uses = JeuMapper.class)
public interface EditeurMapper {
Editeur toEntity(EditeurDto editeurDto);
@AfterMapping
default void linkJeux(@MappingTarget Editeur editeur) {
if (editeur.getJeux()==null) {
return;
}
editeur.getJeux().forEach(jeux -> jeux.setEditeur(editeur));
}
EditeurDto toDto(Editeur editeur);
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
Editeur partialUpdate(EditeurDto editeurDto, @MappingTarget Editeur editeur);
}

View file

@ -0,0 +1,16 @@
package fr.clelia.avis.mapper;
import fr.clelia.avis.business.Jeu;
import fr.clelia.avis.dto.JeuDto;
import org.mapstruct.*;
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = MappingConstants.ComponentModel.SPRING)
public interface JeuMapper {
Jeu toEntity(JeuDto jeuDto);
@Mapping(target="editeur", ignore = true)
JeuDto toDto(Jeu jeu);
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
Jeu partialUpdate(JeuDto jeuDto, @MappingTarget Jeu jeu);
}

View file

@ -1,13 +1,21 @@
package fr.clelia.avis.repository; package fr.clelia.avis.repository;
import fr.clelia.avis.business.Editeur;
import fr.clelia.avis.business.Jeu; import fr.clelia.avis.business.Jeu;
import fr.clelia.avis.business.Plateforme; import fr.clelia.avis.business.Plateforme;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Optional;
@RepositoryRestResource
public interface JeuRepository extends JpaRepository<Jeu, Long> { public interface JeuRepository extends JpaRepository<Jeu, Long> {
@ -29,4 +37,45 @@ public interface JeuRepository extends JpaRepository<Jeu, Long> {
List<Jeu> findJeuxByNomPlateforme(@Param("nomPlateforme") String nomPlateforme); List<Jeu> findJeuxByNomPlateforme(@Param("nomPlateforme") String nomPlateforme);
List<Jeu> findByPlateformesNom(String nom); List<Jeu> findByPlateformesNom(String nom);
Jeu findFirstByNom(String nom);
List<Jeu> findByEditeur(Editeur editeur);
// Navigabilité
List<Jeu> findByEditeurNom(String nom);
List<Jeu> findTop5ByEditeurOrderByDateDeSortieDesc(Editeur editeur);
List<Jeu> findByNomLike(String nom);
List<Jeu> findByNomLikeAndDateDeSortieBetween(String nom, LocalDate dateDebut, LocalDate dateFin);
List<Jeu> findByEditeurAndNomLikeAndDateDeSortieBetween(Editeur editeur, String nom, LocalDate dateDebut, LocalDate dateFin);
/**
* Cette méthode renvoie les jeux disponibles sur la plateforme donnée en paramètre
*
* @param plateforme
* @return
*/
List<Jeu> findAllByPlateformesContaining(Plateforme plateforme);
List<Jeu> findByDateDeSortieBetweenAndEditeur_NomContainsIgnoreCase(LocalDate dateSortieStart, LocalDate dateSortieEnd, String nom);
boolean existsByNom(String nom);
long countByEditeur(Editeur editeur);
long countByEditeurNom(String nom);
@Transactional
long deleteByEditeur(Editeur editeur);
List<Jeu> findByPlateformes(Plateforme plateforme);
Optional<Jeu> findByNomIgnoreCase(String nom);
@RestResource(exported = false)
void delete(Jeu jeu);
} }

View file

@ -1,6 +1,7 @@
package fr.clelia.avis.repository; package fr.clelia.avis.repository;
import fr.clelia.avis.business.Joueur; import fr.clelia.avis.business.Joueur;
import fr.clelia.avis.dto.NbJoueursParAnneeDeNaissance;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
@ -24,4 +25,14 @@ public interface JoueurRepository extends JpaRepository<Joueur, Long> {
AND j.dateDeNaissance > current_date() - 40 year AND j.dateDeNaissance > current_date() - 40 year
""") """)
List<Joueur> findJoueursTrentenaires(); List<Joueur> findJoueursTrentenaires();
// SELECT new : projection
// après le new on précisera le constructeur d'un record ou d'une classe
@Query("""
SELECT new fr.clelia.avis.dto.NbJoueursParAnneeDeNaissance(year(dateDeNaissance), count(*))
FROM Joueur
GROUP BY dateDeNaissance
""")
List<NbJoueursParAnneeDeNaissance> findNbJoueursParAnneeDeNaissance();
} }

View file

@ -1,6 +1,7 @@
package fr.clelia.avis.service; package fr.clelia.avis.service;
import fr.clelia.avis.business.Editeur; import fr.clelia.avis.business.Editeur;
import fr.clelia.avis.dto.EditeurDto;
import java.util.List; import java.util.List;
@ -13,4 +14,10 @@ public interface EditeurService {
Editeur recupererEditeur(Long id); Editeur recupererEditeur(Long id);
Editeur ajouterEditeur(Editeur editeur); Editeur ajouterEditeur(Editeur editeur);
EditeurDto mettreAJourEditeurPartiellement(Long id, EditeurDto editeurDto);
EditeurDto mettreAJourEditeur(Long id, EditeurDto editeurDto);
void supprimerEditeur(Long id);
} }

View file

@ -1,9 +1,12 @@
package fr.clelia.avis.service.impl; package fr.clelia.avis.service.impl;
import fr.clelia.avis.business.Editeur; import fr.clelia.avis.business.Editeur;
import fr.clelia.avis.dto.EditeurDto;
import fr.clelia.avis.exception.EditeurInexistantException; import fr.clelia.avis.exception.EditeurInexistantException;
import fr.clelia.avis.mapper.EditeurMapper;
import fr.clelia.avis.repository.EditeurRepository; import fr.clelia.avis.repository.EditeurRepository;
import fr.clelia.avis.service.EditeurService; import fr.clelia.avis.service.EditeurService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -12,16 +15,18 @@ import java.util.List;
//@Component //@Component
@Service @Service
@Transactional @Transactional
@AllArgsConstructor
public class EditeurServiceImpl implements EditeurService { public class EditeurServiceImpl implements EditeurService {
private EditeurRepository editeurRepository; private final EditeurRepository editeurRepository;
private final EditeurMapper editeurMapper;
// Ce constructeur va faire déduire à Spring /*
// qu'il doit d'abord instancier la repo public EditeurServiceImpl(EditeurRepository editeurRepository, EditeurMapper editeurMapper) {
// avant d'instancier le service
public EditeurServiceImpl(EditeurRepository editeurRepository) {
this.editeurRepository = editeurRepository; this.editeurRepository = editeurRepository;
this.editeurMapper = editeurMapper;
} }
*/
@Override @Override
public List<Editeur> recupererEditeurs() { public List<Editeur> recupererEditeurs() {
@ -43,4 +48,24 @@ public class EditeurServiceImpl implements EditeurService {
public Editeur ajouterEditeur(Editeur editeur) { public Editeur ajouterEditeur(Editeur editeur) {
return editeurRepository.save(editeur); return editeurRepository.save(editeur);
} }
@Override
public EditeurDto mettreAJourEditeurPartiellement(Long id, EditeurDto editeurDto) {
Editeur editeur = recupererEditeur(id);
editeur = editeurRepository.save(editeurMapper.partialUpdate(editeurDto, editeur));
return editeurMapper.toDto(editeur);
}
@Override
public EditeurDto mettreAJourEditeur(Long id, EditeurDto editeurDto) {
Editeur editeurMisAJour = editeurMapper.toEntity(editeurDto);
editeurMisAJour.setId(id);
editeurRepository.save(editeurMisAJour);
return editeurMapper.toDto(editeurMisAJour);
}
@Override
public void supprimerEditeur(Long id) {
editeurRepository.delete(recupererEditeur(id));
}
} }

View file

@ -5,6 +5,18 @@ spring.jpa.generate-ddl=true
spring.data.rest.detection-strategy=annotated spring.data.rest.detection-strategy=annotated
spring.data.rest.base-path=/api-autogeneree/ spring.data.rest.base-path=/api-autogeneree/
spring.datasource.url=jdbc:h2:mem:avis
logging.level.org.hibernate.orm.jdbc.bind=trace
spring.profiles.active=dev
#---
spring.config.activate.on-profile=dev
logging.level.root=INFO
logging.level.org.springframework=DEBUG
logging.file.name=log/avis_dev_log
logging.pattern.console= %d %p %c{1.} [%t] %m%n
spring.jpa.show-sql=true spring.jpa.show-sql=true
management.endpoints.web.base-path=/ management.endpoints.web.base-path=/
management.endpoints.web.exposure.include=beans,health,env,configprops management.endpoints.web.exposure.include=beans,health,env,configprops
@ -12,4 +24,10 @@ management.endpoint.health.group.custom.show-components=always
management.endpoint.health.group.custom.show-details=always management.endpoint.health.group.custom.show-details=always
management.endpoint.env.show-values=always management.endpoint.env.show-values=always
management.endpoint.configprops.show-values=always management.endpoint.configprops.show-values=always
spring.datasource.url=jdbc:h2:mem:avis
#---
spring.config.activate.on-profile=prod
logging.level.root=WARN
logging.level.org.springframework=WARN
logging.file.name=log/avis_prod_log
logging.pattern.console= %d %p %c{1.} [%t] %m%n

View file

@ -0,0 +1,141 @@
package fr.clelia.avis.controller.rest;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.javafaker.Faker;
import fr.clelia.avis.business.Editeur;
import fr.clelia.avis.dto.EditeurDto;
import fr.clelia.avis.service.EditeurService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class EditeurRestControllerITest {
// cet objet imite ce que fait Postwoman, Postman, Insomnia, Bruno, Swagger ou le front
// Angular
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private EditeurService editeurService;
private Faker faker = new Faker(Locale.FRENCH);
@BeforeEach
public void setUp() {
List<Editeur> editeurs = editeurService.recupererEditeurs();
editeurs.forEach(editeur -> { editeurService.supprimerEditeur(editeur.getId()); });
}
//@Test
@RepeatedTest(10)
void testerPostEditeur() throws Exception {
// Arrange
String nomEditeur = faker.company().name();
String nomLogo = faker.company().logo();
EditeurDto editeurDto = new EditeurDto(null, nomEditeur, nomLogo, new ArrayList<>());
MockHttpServletRequestBuilder requestBuilder = post("/api/editeurs")
// On place dans le corps de la requête la version JSON de notre éditeur DTO
.content(objectMapper.writeValueAsString(editeurDto))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON);
// Act
mockMvc.perform(requestBuilder)
// Assert
// On vérifie que le code retour est bien 201 (created)
.andExpect(status().isCreated())
// On vérifie les données présentes dans le corps de la réponse
.andExpect(MockMvcResultMatchers.jsonPath("$.nom").value(nomEditeur))
.andExpect(MockMvcResultMatchers.jsonPath("$.logo").value(nomLogo))
// On affiche en console tout ce qui a été envoyé et reçu
.andDo(MockMvcResultHandlers.print());
}
@Test
void testGetEditeur() throws Exception {
// Arrange
String editeur = "test";
String logo="logo";
Editeur e = editeurService.ajouterEditeur(new Editeur(editeur, logo));
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/api/editeurs/{id}", e.getId());
// Act
mockMvc.perform(requestBuilder)
// Assert
.andExpect(MockMvcResultMatchers.jsonPath("$.nom").value(e.getNom()))
.andExpect(MockMvcResultMatchers.jsonPath("$.logo").value(e.getLogo()))
.andExpect(status().is2xxSuccessful())
.andDo(MockMvcResultHandlers.print());
}
@Test
void testerPostEditeurNomVide() throws Exception {
// Arrange
String nomEditeur = "";
String nomLogo = "logo";
Editeur editeur = new Editeur(nomEditeur, nomLogo);
MockHttpServletRequestBuilder requestBuilder = post("/api/editeurs")
// On place dans le corps de la requête la version JSON de notre éditeur
.content(objectMapper.writeValueAsString(editeur))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON);
// Act
mockMvc.perform(requestBuilder)
// Assert
// On vérifie que le code retour est bien 400 (bad request)
.andExpect(status().isBadRequest())
// On affiche en console tout ce qui a été envoyé et reçu
.andDo(MockMvcResultHandlers.print());
}
@Test
void testGetEditeurs() throws Exception {
String nomEditeur1 = "testEditeur1";
String logoEditeur1 = "logoEditeur1";
String nomEditeur2 = "testEditeur2";
String logoEditeur2 = "logoEditeur1";
Editeur editeur1 = new Editeur(nomEditeur1, logoEditeur1);
Editeur editeur2 = new Editeur(nomEditeur2, logoEditeur2);
editeurService.ajouterEditeur(editeur1);
editeurService.ajouterEditeur(editeur2);
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/api/editeurs");
mockMvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.jsonPath("$[0].id").isNumber())
.andExpect(MockMvcResultMatchers.jsonPath("$[0].nom").value(nomEditeur1))
.andExpect(MockMvcResultMatchers.jsonPath("$[0].logo").value(logoEditeur1))
.andExpect(MockMvcResultMatchers.jsonPath("$[1].id").isNumber())
.andExpect(MockMvcResultMatchers.jsonPath("$[1].nom").value(nomEditeur2))
.andExpect(MockMvcResultMatchers.jsonPath("$[1].logo").value(logoEditeur2))
.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(2));
}
}

View file

@ -0,0 +1,32 @@
package fr.clelia.avis.repository;
import fr.clelia.avis.business.Editeur;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
public class EditeurRepositoryTest {
@Autowired
private EditeurRepository editeurRepository;
@Test
void testSaveEditeur() {
// Arrange
String nom = "test";
String logo = "logo";
Editeur editeur = new Editeur(nom, logo);
// Act
Editeur editeurEnregistre = editeurRepository.save(editeur);
// Assert
assertThat(editeurEnregistre.getNom()).isEqualTo(nom);
assertThat(editeurEnregistre.getLogo()).isEqualTo(logo);
}
// C'est plutôt intéressant pour tester des méthodes annotées @Query
}

View file

@ -0,0 +1,104 @@
package fr.clelia.avis.service.impl;
import fr.clelia.avis.business.Editeur;
import fr.clelia.avis.dto.EditeurDto;
import fr.clelia.avis.dto.JeuDto;
import fr.clelia.avis.dto.PlateformeDto;
import fr.clelia.avis.exception.EditeurInexistantException;
import fr.clelia.avis.mapper.EditeurMapper;
import fr.clelia.avis.repository.EditeurRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static org.mockito.Mockito.*;
class EditeurServiceImplTest {
@Mock
EditeurRepository editeurRepository;
@Mock
EditeurMapper editeurMapper;
@InjectMocks
EditeurServiceImpl editeurServiceImpl;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void testRecupererEditeurs() {
when(editeurRepository.findAll()).thenReturn(List.of(new Editeur("nom", "logo")));
List<Editeur> result = editeurServiceImpl.recupererEditeurs();
Assertions.assertEquals(List.of(new Editeur("nom", "logo")), result);
}
@Test
void testRecupererEditeursAyantUnNomContenant() {
String nomContenant = "contenant";
when(editeurRepository.findByNomContainingIgnoreCase(anyString())).thenReturn(List.of(new Editeur(nomContenant, "logo")));
List<Editeur> result = editeurServiceImpl.recupererEditeursAyantUnNomContenant("nomRecherche");
Assertions.assertEquals(List.of(new Editeur(nomContenant, "logo")), result);
}
@Test
void testAjouterEditeur() {
// Arrange
String nom = "nom";
String logo = "logo";
Editeur editeur = new Editeur(nom, logo);
when(editeurRepository.save(any(Editeur.class))).thenReturn(editeur);
// Act
Editeur result = editeurServiceImpl.ajouterEditeur(new Editeur(nom, logo));
// Assert
Assertions.assertEquals(editeur, result);
}
@Test
void testRecupererEditeur() {
// Arrange
String nom = "nom";
String logo = "logo";
Editeur editeur = new Editeur(nom, logo);
when(editeurRepository.findById(any(Long.class))).thenReturn(Optional.of(editeur));
List<JeuDto> jeuxDto = new ArrayList<>();
EditeurDto editeurDto = new EditeurDto(null, nom, logo, jeuxDto);
when(editeurMapper.toDto(any(Editeur.class))).thenReturn(editeurDto);
// Act
Editeur result = editeurServiceImpl.recupererEditeur(Long.valueOf(1));
// Assert
Assertions.assertEquals(editeur, result);
}
// TODO écrire les deux méthodes de test manquantes
@Test
void testSupprimerEditeur() {
// Arrange
when(editeurRepository.findById(any(Long.class)))
.thenReturn(Optional.of(new Editeur("nom", "logo")))
.thenReturn(Optional.empty());
// Act
editeurServiceImpl.supprimerEditeur(Long.valueOf(1));
// Assert
Assertions.assertThrows(EditeurInexistantException.class, () -> editeurServiceImpl.supprimerEditeur(Long.valueOf(1)));
}
}
//Generated with love by TestMe :) Please raise issues & feature requests at: https://weirddev.com/forum#!/testme

View file

@ -0,0 +1,13 @@
spring.application.name=avis
spring.jpa.generate-ddl=true
spring.data.rest.detection-strategy=annotated
spring.data.rest.base-path=/api-autogeneree/
spring.jpa.show-sql=false
spring.datasource.url=jdbc:h2:mem:avis_test
logging.level.org.hibernate.orm.jdbc.bind=trace