From 9b6b8d6c80f2d9c81566924291282460256ed6e2 Mon Sep 17 00:00:00 2001 From: Vu Tuan Minh Date: Mon, 13 Oct 2025 00:17:51 +0200 Subject: [PATCH] Finished and tested --- README.md | 89 +++++++++-- .../fr/istic/taa/jaxrs/DAO/ReponseDAO.java | 8 - .../fr/istic/taa/jaxrs/TestApplication.java | 1 - .../java/fr/istic/taa/jaxrs/domain/Pet.java | 44 ------ .../fr/istic/taa/jaxrs/metier/Question.java | 2 + .../java/fr/istic/taa/jaxrs/metier/Quizz.java | 2 + .../fr/istic/taa/jaxrs/metier/Session.java | 6 +- .../istic/taa/jaxrs/metier/Utilisateur.java | 2 + .../fr/istic/taa/jaxrs/rest/PetResource.java | 38 ----- .../taa/jaxrs/rest/QuestionResource.java | 139 ++++++++++++++---- .../istic/taa/jaxrs/rest/QuizzResource.java | 89 +++++++++-- .../istic/taa/jaxrs/rest/SessionResource.java | 80 ++++++++-- .../istic/taa/jaxrs/rest/SwaggerResource.java | 36 +++-- .../taa/jaxrs/rest/UtilisateurResource.java | 80 ++++++++-- src/main/resources/swagger/index.html | 92 ++++++++++++ 15 files changed, 535 insertions(+), 173 deletions(-) delete mode 100644 src/main/java/fr/istic/taa/jaxrs/domain/Pet.java delete mode 100644 src/main/java/fr/istic/taa/jaxrs/rest/PetResource.java create mode 100644 src/main/resources/swagger/index.html diff --git a/README.md b/README.md index a46156d..bec768c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,75 @@ # TP TAA - ## TP1 - TP2 ### Compte rendu +Dans ce TP REST, nous avons implémenté les éléments suivants: +- Mapper pour convertir les entités vers les DTO et inversement +- Lombok afin de simplifier le code et éviter le code boilerplate (getters, setters, constructeurs, etc.) +- API REST pour exposer les différentes ressources +- Swagger UI pour documenter et tester facilement toutes les méthodes de l’API. +- DTO (Data Transfer Objects) afin d’éviter les problèmes de boucle infinie liés aux relations bidirectionnelles entre entités. -#### Utilisateur Resource +#### Execution du projet +Recharger les dépendances Maven : +`mvn clean install` + +Lancer la base de données (Linux) : `./run-hsqldb-server.sh` + +Lancer le serveur par fichier `src/main/java/fr.istic.taa.jaxrs/RestServer.java` + +Après le serveur est lacné, on a testé des appels API avec Insomnia et interface Swagger est accessible via: +`http://localhost:8080/api` + +#### Diagramme de classe +```mermaid +classDiagram + Utilisateur "*" -- "*" Session + Session "*" -- "*" Quizz + Quizz "1" -- "1..*" Question + Utilisateur "1" -- "*" Quizz + Question "1" -- "1" Reponse + Reponse <|-- ReponseCourte + Reponse <|-- Choix + + class Utilisateur { + -id : int + -name : String + -session : List<Session> + -email : String + -password : String + -quizzs : String + } + class Session{ + -id : int + -codePIN : int + -quizzs : List<Quizz> + -utilisateurs : List<Utilisateur> + -theme : String + } + class Quizz{ + -session: Session + -id : int + -utilisateur: Utilisateur + } + class Reponse{ + -id : int + -question: Question + -reponses : ArrayList<String> + } + class Choix{ + -choix : ArrayList<String> + } + class ReponseCourte{ + -value : String + } + class Question{ + -id : int + -reponse: Reponse + } +``` + +##### Utilisateur Resource | Methode | URL | Description | |---------|---------------------------------------------------|-------------------------------------------------------| | GET | `/utilisateur` | Retourne toute la liste de l'utilisateur | @@ -16,7 +80,7 @@ | PUT | `/utilisateur/{user_id}/add_session/{session_id}` | Ajoute un nouveau session à la liste de l'utilisateur | | DELETE | `/utilisteur/{user_id}/delete` | Supprime l'utilisateur | -#### Session Resource +##### Session Resource | Methode | URL | Description | |---------|--------------------------------------|------------------------------------| | GET | `/session` | Retourne toute la liste du session | @@ -25,29 +89,28 @@ | GET | `/session/{session_id}/utilisateurs` | Retourne la liste de l'utilisateur | | DELETE | `/session/{session_id}/delete` | Supprime la session | -#### Quizz Resource -| Methode | URL | Description | -|---------|------------------------------------------------|----------------------------------------| -| GET | `/quizz` | Retourne toute la liste du quizz | -| GET | `/quizz/{quizz_id}/add_question/{question_id}` | Ajoute une nouvuelle question au quizz | -| DELETE | `/quizz/{quizz_id}/delete` | Suprrime le quizz | -| PUT | `/quizz/{quizz_id}/deleteQ` | Supprime toute les questions de quizz | +##### Quizz Resource +| Methode | URL | Description | +|---------|------------------------------------------------|-----------------------------------------| +| GET | `/quizz` | Retourne toute la liste du quizz | +| GET | `/quizz/{quizz_id}/questions | Retourne toutes questions du quizz | +| GET | `/quizz/{quizz_id}/add_question/{question_id}` | Ajoute une nouvuelle question au quizz | +| DELETE | `/quizz/{quizz_id}/delete` | Suprrime le quizz | +| PUT | `/quizz/{quizz_id}/deleteQ` | Supprime toute les questions de quizz | -#### Question Resource +##### Question Resource | Methode | URL | Description | |----------|----------------------------------------------------|-----------------------------------------------------------------------| | GET | `/question` | Retourne toute la liste du question | | POST | `/question/addQuestion` | Créer une question | | PUT | `/question/{question_id}/changeQuestion` | Change l'énoncé de la question | | GET | `/question/{question_id}/getReponse` | retourne l'objet Reponse | -| PUT | `/question/{question_id}/addReponse` | Ajoute une réponse à Reponse | | DELETE | `/question/{question_id}/deletReponses` | supprime toute les réponse correct | | PUT | `/question/{question_id}/addReponse` | Ajoute une réponse à Reponse | | PUT | `/question/{question_id}/setReponse/choix` | créer un nouvel objet Reponse de type Choix pour la question | | PUT | `/question/{question_id}/setReponse/reponseCourte` | créer un nouvel objet Reponse de type reponse courte pour la question | | PUT | `/question/{question_id}/addChoix` | rajoute un choix si Reponse est de type Choix | - ## Auteurs - Tuan Minh VU diff --git a/src/main/java/fr/istic/taa/jaxrs/DAO/ReponseDAO.java b/src/main/java/fr/istic/taa/jaxrs/DAO/ReponseDAO.java index cb27451..05bad26 100644 --- a/src/main/java/fr/istic/taa/jaxrs/DAO/ReponseDAO.java +++ b/src/main/java/fr/istic/taa/jaxrs/DAO/ReponseDAO.java @@ -8,12 +8,4 @@ public class ReponseDAO extends AbstractJpaDao { this.setClass(Reponse.class); } - /*public List getGoodResponses(){ - EntityTransaction t=em.getTransaction(); - t.begin(); - Query query=em.createQuery("select r from Reponse r where r.reponses"); - List lString=query.getResultList(); - t.commit(); - return lString; - }*/ } diff --git a/src/main/java/fr/istic/taa/jaxrs/TestApplication.java b/src/main/java/fr/istic/taa/jaxrs/TestApplication.java index 24bcd2a..13ca60d 100644 --- a/src/main/java/fr/istic/taa/jaxrs/TestApplication.java +++ b/src/main/java/fr/istic/taa/jaxrs/TestApplication.java @@ -34,7 +34,6 @@ public class TestApplication extends Application { final Set> clazzes = new HashSet>(); clazzes.add(OpenApiResource.class); - clazzes.add(PetResource.class); clazzes.add(QuestionResource.class); clazzes.add(QuizzResource.class); clazzes.add(UtilisateurResource.class); diff --git a/src/main/java/fr/istic/taa/jaxrs/domain/Pet.java b/src/main/java/fr/istic/taa/jaxrs/domain/Pet.java deleted file mode 100644 index c8738b9..0000000 --- a/src/main/java/fr/istic/taa/jaxrs/domain/Pet.java +++ /dev/null @@ -1,44 +0,0 @@ -package fr.istic.taa.jaxrs.domain; - -import java.util.ArrayList; -import java.util.List; - -import io.swagger.v3.oas.models.tags.Tag; -import jakarta.xml.bind.annotation.XmlElement; -import jakarta.xml.bind.annotation.XmlElementWrapper; -import jakarta.xml.bind.annotation.XmlRootElement; - -@XmlRootElement(name = "Pet") -public class Pet { - private long id; - private String name; - private List tags = new ArrayList(); - - @XmlElement(name = "id") - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - - @XmlElement(name = "name") - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @XmlElementWrapper(name = "tags") - @XmlElement(name = "tag") - public List getTags() { - return tags; - } - - public void setTags(List tags) { - this.tags = tags; - } -} \ No newline at end of file diff --git a/src/main/java/fr/istic/taa/jaxrs/metier/Question.java b/src/main/java/fr/istic/taa/jaxrs/metier/Question.java index 22edd9b..700a233 100644 --- a/src/main/java/fr/istic/taa/jaxrs/metier/Question.java +++ b/src/main/java/fr/istic/taa/jaxrs/metier/Question.java @@ -1,6 +1,7 @@ package fr.istic.taa.jaxrs.metier; import jakarta.persistence.*; +import jakarta.xml.bind.annotation.XmlRootElement; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -11,6 +12,7 @@ import java.io.Serializable; @Getter @Setter @NoArgsConstructor +@XmlRootElement public class Question implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/fr/istic/taa/jaxrs/metier/Quizz.java b/src/main/java/fr/istic/taa/jaxrs/metier/Quizz.java index 4a312bc..754306c 100644 --- a/src/main/java/fr/istic/taa/jaxrs/metier/Quizz.java +++ b/src/main/java/fr/istic/taa/jaxrs/metier/Quizz.java @@ -1,6 +1,7 @@ package fr.istic.taa.jaxrs.metier; import jakarta.persistence.*; +import jakarta.xml.bind.annotation.XmlRootElement; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -13,6 +14,7 @@ import java.util.List; @Getter @Setter @NoArgsConstructor +@XmlRootElement public class Quizz implements Serializable { @Id @GeneratedValue diff --git a/src/main/java/fr/istic/taa/jaxrs/metier/Session.java b/src/main/java/fr/istic/taa/jaxrs/metier/Session.java index 1c2ef0f..19846ef 100644 --- a/src/main/java/fr/istic/taa/jaxrs/metier/Session.java +++ b/src/main/java/fr/istic/taa/jaxrs/metier/Session.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.List; import jakarta.persistence.*; +import jakarta.xml.bind.annotation.XmlRootElement; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -13,6 +14,7 @@ import lombok.Setter; @Getter @Setter @NoArgsConstructor +@XmlRootElement public class Session implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -21,10 +23,10 @@ public class Session implements Serializable { @Column(unique=true) private int codePIN; - @OneToMany(mappedBy="session") + @OneToMany(mappedBy="session", fetch = FetchType.EAGER) private List quizzs = new ArrayList<>(); - @ManyToMany(mappedBy = "session") + @ManyToMany(mappedBy = "session", fetch = FetchType.EAGER) private List utilisateurs = new ArrayList<>(); private String theme; } \ No newline at end of file diff --git a/src/main/java/fr/istic/taa/jaxrs/metier/Utilisateur.java b/src/main/java/fr/istic/taa/jaxrs/metier/Utilisateur.java index ff9ab3c..dc9e8dd 100644 --- a/src/main/java/fr/istic/taa/jaxrs/metier/Utilisateur.java +++ b/src/main/java/fr/istic/taa/jaxrs/metier/Utilisateur.java @@ -1,6 +1,7 @@ package fr.istic.taa.jaxrs.metier; import jakarta.persistence.*; +import jakarta.xml.bind.annotation.XmlRootElement; import lombok.*; import java.io.Serializable; import java.util.ArrayList; @@ -10,6 +11,7 @@ import java.util.List; @Getter @Setter @NoArgsConstructor +@XmlRootElement public class Utilisateur implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/fr/istic/taa/jaxrs/rest/PetResource.java b/src/main/java/fr/istic/taa/jaxrs/rest/PetResource.java deleted file mode 100644 index 3087395..0000000 --- a/src/main/java/fr/istic/taa/jaxrs/rest/PetResource.java +++ /dev/null @@ -1,38 +0,0 @@ -package fr.istic.taa.jaxrs.rest; - -import fr.istic.taa.jaxrs.domain.Pet; -import io.swagger.v3.oas.annotations.Parameter; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.Response; - -@Path("pet") -@Produces({"application/json", "application/xml"}) -public class PetResource { - - @GET - @Path("/{petId}") - public Pet getPetById(@PathParam("petId") Long petId) { - // return pet - return new Pet(); - } - - @GET - @Path("/") - public Pet getPet(Long petId) { - return new Pet(); - } - - - @POST - @Consumes("application/json") - public Response addPet( - @Parameter(description = "Pet object that needs to be added to the store", required = true) Pet pet) { - // add pet - return Response.ok().entity("SUCCESS").build(); - } -} \ No newline at end of file diff --git a/src/main/java/fr/istic/taa/jaxrs/rest/QuestionResource.java b/src/main/java/fr/istic/taa/jaxrs/rest/QuestionResource.java index 8daccbe..04a356f 100644 --- a/src/main/java/fr/istic/taa/jaxrs/rest/QuestionResource.java +++ b/src/main/java/fr/istic/taa/jaxrs/rest/QuestionResource.java @@ -9,12 +9,13 @@ import fr.istic.taa.jaxrs.metier.Choix; import fr.istic.taa.jaxrs.metier.Question; import fr.istic.taa.jaxrs.metier.Reponse; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; -import jakarta.ws.rs.PATCH; import jakarta.ws.rs.PUT; import jakarta.ws.rs.POST; import jakarta.ws.rs.DELETE; @@ -32,14 +33,28 @@ public class QuestionResource { private final QuestionMapper mapper = QuestionMapper.INSTANCE; @GET - public List listQuestion(){ + @Operation(summary = "List all questions", + tags = {"Questions"}, + description = "List all questions.", + responses = { + @ApiResponse(description = "List of all questions", content = @Content( + array = @ArraySchema(schema = @Schema(implementation = Question.class)))) + } + ) + public List listQuestion() { List questions = questionDAO.findAll(); - return questions; + return mapper.toDTOs(questions); } @POST @Path("/addQuestion") - public Response addQuestion(QuestionDTO dto){ + @Operation(summary = "Create a new question", + tags = {"Questions"}, + description = "Create a new question", + responses = {@ApiResponse(responseCode = "201", description = "Question added.")} + ) + public Response addQuestion( + @Parameter(description = "The question details to be added", required = true) QuestionDTO dto) { Question question = mapper.toEntity(dto); questionDAO.create(question); return Response.status(Response.Status.CREATED).entity("Question ajouté avec Succès : \"" + dto.getQuestion() + "\"").build(); @@ -47,21 +62,43 @@ public class QuestionResource { @PUT @Path("/{question_id}/changeQuestion/") - public Response changeQuestion(@PathParam("question_id") Integer id, String newQuestion){ + @Operation(summary = "Update the text of a question", + tags = {"Questions"}, + description = "Update the text of a question.", + responses = { + @ApiResponse(responseCode = "201", description = "Question answer updated."), + @ApiResponse(responseCode = "404", description = "Question not found") + } + ) + public Response changeQuestion( + @Parameter(description = "ID of the question to modify", required = true) @PathParam("question_id") Integer id, + @Parameter(description = "New answer for the question", required = true) String newQuestion) { Question question = questionDAO.findById(id); - if(question == null){ + if (question == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - questionDAO.changeQuestion(newQuestion,id); + questionDAO.changeQuestion(newQuestion, id); return Response.status(Response.Status.CREATED).entity("Question mis à jour : " + newQuestion).build(); } @GET @Path("/{question_id}/getReponse/") - public Response getReponse(@PathParam("question_id") Integer id){ + @Operation(summary = "Get the correct answer for question", + tags = {"Questions"}, + description = "Get the correct answer for question by ID of question.", + responses = { + @ApiResponse(description = "The correct answer", content = @Content( + schema = @Schema(implementation = Reponse.class))), + @ApiResponse(responseCode = "404", description = "Question not found") + } + ) + public Response getReponse( + @Parameter(description = "ID of the question", required = true) + @PathParam("question_id") Integer id) { + Question question = questionDAO.findById(id); - if(question == null){ + if (question == null) { return Response.status(Response.Status.NOT_FOUND).build(); } Reponse result = question.getReponse(); @@ -71,21 +108,42 @@ public class QuestionResource { @PUT @Path("/{question_id}/addReponse/") - public Response addReponse(@PathParam("question_id") Integer id, String reponse){ + @Operation(summary = "Add/Update the correct answer for question", + tags = {"Questions"}, + description = "Add/Update the correct answer for question", + responses = { + @ApiResponse(responseCode = "201", description = "Answer text updated."), + @ApiResponse(responseCode = "404", description = "Question not found") + } + ) + public Response addReponse( + @Parameter(description = "ID of the question", required = true) @PathParam("question_id") Integer id, + @Parameter(description = "The correct answer", required = true) String reponse) { + Question question = questionDAO.findById(id); - if(question == null){ + if (question == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - questionDAO.addReponse(reponse,id); - return Response.status(Response.Status.CREATED).entity("Reponse correct \""+reponse+"\" ajouté à la question " + id).build(); + questionDAO.addReponse(reponse, id); + return Response.status(Response.Status.CREATED).entity("Reponse correct \"" + reponse + "\" ajouté à la question " + id).build(); } @DELETE @Path("/{question_id}/deletReponses/") - public Response addReponse(@PathParam("question_id") Integer id){ + @Operation(summary = "Removes the correct response.", + tags = {"Questions"}, + description = "Removes the correct response.", + responses = { + @ApiResponse(responseCode = "201", description = "Response deleted."), + @ApiResponse(responseCode = "404", description = "Question not found") + } + ) + public Response deleteReponses( + @Parameter(description = "ID of the question", required = true) + @PathParam("question_id") Integer id) { Question question = questionDAO.findById(id); - if(question == null){ + if (question == null) { return Response.status(Response.Status.NOT_FOUND).build(); } questionDAO.deleteReponse(id); @@ -95,38 +153,69 @@ public class QuestionResource { @PUT @Path("/{question_id}/setReponse/choix") - public Response setChoix(@PathParam("question_id") Integer id){ + @Operation(summary = "Set question type to multiple choice", + tags = {"Questions"}, + description = "Set question type to multiple choice type. It could override old answers", + responses = { + @ApiResponse(responseCode = "201", description = "Question type set to multiple choice."), + @ApiResponse(responseCode = "404", description = "Question not found") + } + ) + public Response setChoix( + @Parameter(description = "ID of the question", required = true) + @PathParam("question_id") Integer id) { Question question = questionDAO.findById(id); - if(question == null){ + if (question == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - questionDAO.setChoix(id); + questionDAO.setChoix(id); return Response.status(Response.Status.CREATED).entity("Reponses à choix multiple mise sur la question " + id).build(); } @PUT @Path("/{question_id}/setReponse/reponseCourte") - public Response setReponseCourte(@PathParam("question_id") Integer id){ + @Operation(summary = "Set question type to short answer", + tags = {"Questions"}, + description = "Set question type to multiple short answer. It could override old answers", + responses = { + @ApiResponse(responseCode = "201", description = "Question type set to short answer."), + @ApiResponse(responseCode = "404", description = "Question not found") + } + ) + public Response setReponseCourte( + @Parameter(description = "ID of the question", required = true) + @PathParam("question_id") Integer id) { Question question = questionDAO.findById(id); - if(question == null){ + if (question == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - questionDAO.setReponseCourte(id); + questionDAO.setReponseCourte(id); return Response.status(Response.Status.CREATED).entity("Reponses courte mise sur la question " + id).build(); } @PUT @Path("/{question_id}/AddChoix") - public Response addChoix(@PathParam("question_id") Integer id, String choix){ + @Operation(summary = "Add a choice option to a multiple-choice question", + tags = {"Questions"}, + description = "Adds a new choice option to the question (Type Multiple Choice obligatoire)", + responses = { + @ApiResponse(responseCode = "201", description = "Choice added."), + @ApiResponse(responseCode = "404", description = "Question not found"), + @ApiResponse(responseCode = "400", description = "Wrong type") + } + ) + public Response addChoix( + @Parameter(description = "ID of the question", required = true) @PathParam("question_id") Integer id, + @Parameter(description = "The choice", required = true) String choix){ Question question = questionDAO.findById(id); - if(question == null){ + if (question == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - if(question.getReponse() instanceof Choix){ + if (question.getReponse() instanceof Choix) { return Response.status(Response.Status.EXPECTATION_FAILED).build(); } - questionDAO.addChoix(id, choix); + questionDAO.addChoix(id, choix); return Response.status(Response.Status.CREATED).entity("Reponses courte mise sur la question " + id).build(); } diff --git a/src/main/java/fr/istic/taa/jaxrs/rest/QuizzResource.java b/src/main/java/fr/istic/taa/jaxrs/rest/QuizzResource.java index eb006ea..488c766 100644 --- a/src/main/java/fr/istic/taa/jaxrs/rest/QuizzResource.java +++ b/src/main/java/fr/istic/taa/jaxrs/rest/QuizzResource.java @@ -8,6 +8,12 @@ import fr.istic.taa.jaxrs.Mapper.QuestionMapper; import fr.istic.taa.jaxrs.Mapper.QuizzMapper; import fr.istic.taa.jaxrs.metier.Question; import fr.istic.taa.jaxrs.metier.Quizz; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.ws.rs.*; import jakarta.ws.rs.core.Response; @@ -18,14 +24,25 @@ import java.util.List; @Produces({"application/json", "application/xml"}) public class QuizzResource { public final QuizzMapper mapper = QuizzMapper.INSTANCE; - public final QuizzDAO quizzDAO= new QuizzDAO(); + public final QuizzDAO quizzDAO = new QuizzDAO(); public final QuestionDAO questionDAO = new QuestionDAO(); @GET @Path("/{quizz_id}") - public Response getQuizzById(@PathParam("quizz_id") Integer quizzId) { + @Operation(summary = "Get a quizz by ID", + tags = {"Quizzes"}, + description = "Get a quizz by ID", + responses = { + @ApiResponse(description = "The quizz by ID", content = @Content( + schema = @Schema(implementation = QuizzDTO.class))), + @ApiResponse(responseCode = "404", description = "Quizz not found") + } + ) + public Response getQuizzById( + @Parameter(description = "ID of the quizz to fetch", required = true) + @PathParam("quizz_id") Integer quizzId) { Quizz quizz = quizzDAO.findById(quizzId); - if (quizz == null) { + if (quizz == null) { return Response.status(Response.Status.NOT_FOUND).build(); } QuizzDTO dto = mapper.toDTO(quizz); @@ -34,12 +51,23 @@ public class QuizzResource { @GET @Path("/{quizz_id}/questions") - public Response getQuestions(@PathParam("quizz_id") Integer quizzId) { + @Operation(summary = "List all questions for a quizz", + tags = {"Quizzes"}, + description = "List all questions by quizz ID.", + responses = { + @ApiResponse(description = "List of questions", content = @Content( + array = @ArraySchema(schema = @Schema(implementation = QuestionDTO.class)))), + @ApiResponse(responseCode = "404", description = "Quizz not found") + } + ) + public Response getQuestions( + @Parameter(description = "ID of the quizz", required = true) + @PathParam("quizz_id") Integer quizzId) { Quizz quizz = quizzDAO.findById(quizzId); if (quizz == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - List questionList= quizz.getQuestions(); + List questionList = quizz.getQuestions(); List dtos = QuestionMapper.INSTANCE.toDTOs(questionList); return Response.status(Response.Status.OK).entity(dtos).build(); @@ -47,27 +75,68 @@ public class QuizzResource { @PUT @Path("/{quizz_id}/add_question/{question_id}") - public Response addQuestion(@PathParam("quizz_id") Integer quizzId, @PathParam("question_id") Integer questionId) { + @Operation(summary = "Add a question to a quizz", + tags = {"Quizzes"}, + description = "Add question to quizz by quizz ID and question ID.", + responses = { + @ApiResponse(responseCode = "200", description = "Question added successfully", + content = @Content(schema = @Schema(implementation = QuizzDTO.class))), + @ApiResponse(responseCode = "404", description = "Quizz or Question not found") + } + ) + public Response addQuestion( + @Parameter(description = "ID of the quizz to update", required = true) @PathParam("quizz_id") Integer quizzId, + @Parameter(description = "ID of the question to add", required = true) @PathParam("question_id") Integer questionId) { Quizz quizz = quizzDAO.findById(quizzId); Question question = questionDAO.findById(questionId); if (quizz == null || question == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - quizzDAO.addQuestion(quizzId,questionId); + quizzDAO.addQuestion(quizzId, questionId); quizzDAO.update(quizz); QuizzDTO quizzDTO = mapper.toDTO(quizz); - return Response.status(Response.Status.OK).entity(quizzDTO).build(); + return Response.status(Response.Status.OK).entity(quizzDTO).build(); } @PUT @Path("/{quizz_id}/deleteQ") - public Response deleteQuestion(@PathParam("quizz_id") Integer quizzId) { + @Operation(summary = "Remove all questions from a quizz", + tags = {"Quizzes"}, + description = "Removes all questions by ID of quizz.", + responses = { + @ApiResponse(responseCode = "200", description = "Removed all questions"), + @ApiResponse(responseCode = "404", description = "Quizz not found")} + ) + public Response deleteQuestion( + @Parameter(description = "ID of the quizz", required = true) + @PathParam("quizz_id") Integer quizzId) { Quizz quizz = quizzDAO.findById(quizzId); if (quizz == null) { return Response.status(Response.Status.NOT_FOUND).build(); } quizzDAO.deleteAllQustion(quizzId); quizzDAO.update(quizz); - return Response.status(Response.Status.OK).build(); + return Response.status(Response.Status.OK).build(); + } + + @DELETE + @Path("/{quizz_id}/delete") + @Operation(summary = "Delete a Quizz", + tags = {"Quizzes"}, + description = "Deletes a Quizz by ID.", + responses = { + @ApiResponse(responseCode = "200", description = "Quizz deleted."), + @ApiResponse(responseCode = "404", description = "Quizz not found.") + } + ) + public Response deleteQuizz( + @Parameter(description = "ID of the Quizz to delete", required = true) + @PathParam("quizz_id") Integer quizzId) { + Quizz quizz = quizzDAO.findById(quizzId); + if (quizz == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + quizzDAO.delete(quizz); + return Response.status(Response.Status.OK).build(); } } diff --git a/src/main/java/fr/istic/taa/jaxrs/rest/SessionResource.java b/src/main/java/fr/istic/taa/jaxrs/rest/SessionResource.java index 2691cb7..4ed3380 100644 --- a/src/main/java/fr/istic/taa/jaxrs/rest/SessionResource.java +++ b/src/main/java/fr/istic/taa/jaxrs/rest/SessionResource.java @@ -10,6 +10,12 @@ import fr.istic.taa.jaxrs.Mapper.SessionMapper; import fr.istic.taa.jaxrs.Mapper.UtilisateurMapper; import fr.istic.taa.jaxrs.metier.Session; import fr.istic.taa.jaxrs.metier.Utilisateur; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.ws.rs.*; import jakarta.ws.rs.core.Response; @@ -23,14 +29,33 @@ public class SessionResource { private final SessionMapper mapper = SessionMapper.INSTANCE; @GET - public List listSession() { + @Operation(summary = "List all session", tags = {"Sessions"}, + description = "Get a list of all sessions in BDD", + responses = {@ApiResponse(description = "List of sessions", content = @Content( + schema = @Schema(implementation = Session.class) + )) + } + ) + public List listSession() { List sessions = sessionDAO.findAll(); - return sessions; + return mapper.toDTOs(sessions); } @GET @Path("/{id}") - public Response getSession(@PathParam("id") Integer id) { + @Operation(summary = "Get session by ID", + tags = {"Sessions"}, + description = "Get session by ID.", + responses = { + @ApiResponse(description = "Session", content = @Content( + schema = @Schema(implementation = SessionDTO.class) + )), + @ApiResponse(responseCode = "404", description = "Session not found") + } + ) + public Response getSession( + @Parameter(description = "ID of the session to fetch", required = true) + @PathParam("id") Integer id) { Session session = sessionDAO.findById(id); if (session == null) { return Response.status(Response.Status.NOT_FOUND).build(); @@ -41,35 +66,70 @@ public class SessionResource { @GET @Path("/{session_id}/quizzs") - public Response getQuizzs(@PathParam("session_id") Integer sessionId) { + @Operation(summary = "Get all quizzes for a session", + tags = {"Sessions"}, + description = "Get all quizzes for a session by ID of session.", + responses = { + @ApiResponse(description = "List of quizzes", content = @Content( + array = @ArraySchema(schema = @Schema(implementation = QuizzDTO.class)) + )), + @ApiResponse(responseCode = "404", description = "Session not found") + } + ) + public Response getQuizzs( + @Parameter(description = "ID of the session to get quizzes", required = true) + @PathParam("session_id") Integer sessionId) { Session session = sessionDAO.findById(sessionId); if (session == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - List results= QuizzMapper.INSTANCE.toDTOs(session.getQuizzs()); + List results = QuizzMapper.INSTANCE.toDTOs(session.getQuizzs()); return Response.status(Response.Status.OK).entity(results).build(); } @GET @Path("/{session_id}/utilisateurs") - public Response getUtilisateurs(@PathParam("session_id") Integer sessionId) { + @Operation(summary = "Get all users for a session", + tags = {"Sessions"}, + description = "Get all users for a session by ID of session.", + responses = { + @ApiResponse(description = "List of users", content = @Content( + array = @ArraySchema(schema = @Schema(implementation = UtilisateurDTO.class)) + )), + @ApiResponse(responseCode = "404", description = "Session not found") + } + ) + public Response getUtilisateurs( + @Parameter(description = "ID of the session to get users", required = true) + @PathParam("session_id") Integer sessionId) { + Session session = sessionDAO.findById(sessionId); if (session == null) { return Response.status(Response.Status.NOT_FOUND).build(); } - List utilisateurList= session.getUtilisateurs(); - List dtos= UtilisateurMapper.INSTANCE.toDTOs(utilisateurList); + List utilisateurList = session.getUtilisateurs(); + List dtos = UtilisateurMapper.INSTANCE.toDTOs(utilisateurList); return Response.status(Response.Status.OK).entity(dtos).build(); } @DELETE @Path("/{session_id}/delete") - public Response deleteSession(@PathParam("session_id") Integer sessionId) { + @Operation(summary = "Delete a session", + tags = {"Sessions"}, + description = "Deletes a session by ID of session.", + responses = { + @ApiResponse(responseCode = "200", description = "Session deleted"), + @ApiResponse(responseCode = "404", description = "Session not found") + } + ) + public Response deleteSession( + @Parameter(description = "ID of the session to delete", required = true) + @PathParam("session_id") Integer sessionId) { Session session = sessionDAO.findById(sessionId); if (session == null) { return Response.status(Response.Status.NOT_FOUND).build(); } sessionDAO.delete(session); - return Response.status(Response.Status.OK).entity("Réussi de supprimer").build(); + return Response.status(Response.Status.OK).entity("Session deleted").build(); } } diff --git a/src/main/java/fr/istic/taa/jaxrs/rest/SwaggerResource.java b/src/main/java/fr/istic/taa/jaxrs/rest/SwaggerResource.java index 82780e3..724a5fc 100644 --- a/src/main/java/fr/istic/taa/jaxrs/rest/SwaggerResource.java +++ b/src/main/java/fr/istic/taa/jaxrs/rest/SwaggerResource.java @@ -1,6 +1,7 @@ package fr.istic.taa.jaxrs.rest; import java.io.IOException; +import java.io.InputStream; import java.nio.file.FileSystems; import java.nio.file.Files; import java.util.logging.Logger; @@ -8,28 +9,37 @@ import java.util.logging.Logger; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.core.Response; + @Path("/api") public class SwaggerResource { private static final Logger logger = Logger.getLogger(SwaggerResource.class.getName()); + private static final String SWAGGER_BASE_PATH = "swagger/"; + @GET - public byte[] Get1() { - try { - return Files.readAllBytes(FileSystems.getDefault().getPath("src/main/webapp/swagger/index.html")); - } catch (IOException e) { - return null; - } + public Response Get1() { + return getFileContent("index.html"); } @GET @Path("{path:.*}") - public byte[] Get(@PathParam("path") String path) { - try { - return Files.readAllBytes(FileSystems.getDefault().getPath("src/main/webapp/swagger/"+path)); - } catch (IOException e) { - return null; - } + public Response Get(@PathParam("path") String path) { + return getFileContent(path); } -} + private Response getFileContent(String fileName) { + String fullPath = SWAGGER_BASE_PATH + fileName; + try (InputStream is = getClass().getClassLoader().getResourceAsStream(fullPath)) { + if (is == null) { + logger.warning("File not found: " + fullPath); + return Response.status(Response.Status.NOT_FOUND).build(); // HTTP 404 + } + return Response.ok(is.readAllBytes()).build(); + } catch (IOException e) { + logger.severe("Error reading file " + fullPath + ": " + e.getMessage()); + return Response.serverError().build(); // HTTP 500 + } + } +} \ No newline at end of file diff --git a/src/main/java/fr/istic/taa/jaxrs/rest/UtilisateurResource.java b/src/main/java/fr/istic/taa/jaxrs/rest/UtilisateurResource.java index f9e263d..e577707 100644 --- a/src/main/java/fr/istic/taa/jaxrs/rest/UtilisateurResource.java +++ b/src/main/java/fr/istic/taa/jaxrs/rest/UtilisateurResource.java @@ -27,14 +27,31 @@ public class UtilisateurResource { private final UtilisateurMapper mapper = UtilisateurMapper.INSTANCE; @GET - public List listUtilisateur() { + @Operation(summary = "List all users", tags = {"Utilisateurs"}, + description = "Get a list of all users in BDD", + responses = {@ApiResponse(description = "List of users", content = @Content( + schema = @Schema(implementation = Utilisateur.class) + )) + } + ) + public List listUtilisateur() { List utilisateurs = utilisateurDAO.findAll(); - return utilisateurs; + return mapper.toDTOs(utilisateurs); } + //https://stackoverflow.com/questions/9269040/which-http-response-code-for-this-email-is-already-registered @POST @Path("/register") - public Response registerUtilisateur(UtilisateurDTO dto) { + @Operation(summary = "Register a new user", + tags = {"Utilisateurs"}, + description = "Registers a new user.", + responses = { + @ApiResponse(responseCode = "201", description = "Registration succès"), + @ApiResponse(responseCode = "409", description = "Email est déjà registré") + } + ) + public Response registerUtilisateur( + @Parameter(description = "User details for registration", required = true) UtilisateurDTO dto) { String email_verification = dto.getEmail(); Utilisateur existing = utilisateurDAO.findByEmail(email_verification); @@ -51,7 +68,19 @@ public class UtilisateurResource { @POST @Path("/login") - public Response loginUtilisateur(UtilisateurDTO dto) { + @Operation(summary = "Log in a user", + tags = {"Utilisateurs"}, + description = "Authenticates a user and returns the user's details upon success.", + responses = { + @ApiResponse(responseCode = "200", description = "Successful login", content = @Content( + schema = @Schema(implementation = UtilisateurDTO.class) + )), + @ApiResponse(responseCode = "404", description = "Email n'existe pas"), + @ApiResponse(responseCode = "401", description = "Mauvais mdp") + } + ) + public Response loginUtilisateur( + @Parameter(description = "User credentials (email and password)", required = true) UtilisateurDTO dto) { Utilisateur utilisateur = utilisateurDAO.findByEmail(dto.getEmail()); if (utilisateur == null) { @@ -67,7 +96,7 @@ public class UtilisateurResource { @GET @Path("/{id}") @Operation(summary = "Get user by ID", - tags = {"utilisateur"}, + tags = {"Utilisateurs"}, description = "Get User by ID", responses = { @ApiResponse(description = "Utilisateur", content = @Content( @@ -95,7 +124,20 @@ public class UtilisateurResource { @PUT @Path("/{user_id}/add_session/{session_id}") - public Response addSession(@PathParam("user_id") Integer user_id, @PathParam("session_id") Integer session_id) { + @Operation(summary = "Add a session to a user", + tags = {"Utilisateurs"}, + description = "Add a session to a user existed in BDD", + responses = { + @ApiResponse(responseCode = "200", description = "Session added successfully", content = @Content( + schema = @Schema(implementation = UtilisateurDTO.class) // Returns updated user DTO + )), + @ApiResponse(responseCode = "404", description = "User or Session not found"), + @ApiResponse(responseCode = "409", description = "User is already in the session") + } + ) + public Response addSession( + @Parameter(description = "ID of the user") @PathParam("user_id") Integer user_id, + @Parameter(description = "ID of the session to add") @PathParam("session_id") Integer session_id) { SessionDAO sessionDAO = new SessionDAO(); Session existingSession = sessionDAO.findById(session_id); @@ -119,7 +161,19 @@ public class UtilisateurResource { @GET @Path("{user_id}/session") - public Response listSession(@PathParam("user_id") Integer user_id) { + @Operation(summary = "List of all sessions of a user", + tags = {"Utilisateurs"}, + description = "Return a response of all sessions of a user", + responses = { + @ApiResponse(responseCode = "200", description = "List of user's sessions", content = @Content( + schema = @Schema(implementation = SessionDTO.class) + )), + @ApiResponse(responseCode = "404", description = "User not found") + } + ) + public Response listSession( + @Parameter(description = "ID of the user") @PathParam("user_id") Integer user_id) { + Utilisateur utilisateur = utilisateurDAO.findById(user_id); if (utilisateur == null) { return Response.status(Response.Status.NOT_FOUND).build(); @@ -128,10 +182,18 @@ public class UtilisateurResource { List dtos = SessionMapper.INSTANCE.toDTOs(sess); return Response.status(Response.Status.OK).entity(dtos).build(); } - @DELETE @Path("{user_id}/delete") - public Response deleteUtilisateur(@PathParam("user_id") Integer user_id) { + @Operation(summary = "Delete a user", + tags = {"Utilisateurs"}, + description = "Delete a user with ID", + responses = { + @ApiResponse(responseCode = "200", description = "User deleted"), + @ApiResponse(responseCode = "404", description = "User not found") + } + ) + public Response deleteUtilisateur( + @Parameter(description = "ID of the user to delete") @PathParam("user_id") Integer user_id) { Utilisateur existing = utilisateurDAO.findById(user_id); if (existing == null) { return Response.status(Response.Status.NOT_FOUND).build(); diff --git a/src/main/resources/swagger/index.html b/src/main/resources/swagger/index.html new file mode 100644 index 0000000..0eca7eb --- /dev/null +++ b/src/main/resources/swagger/index.html @@ -0,0 +1,92 @@ + + + + + + Swagger UI + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +