23 Commits

Author SHA1 Message Date
trochas
cb685ef004 readme, ajout info sur l'état actuel du projet 2025-10-25 00:07:16 +02:00
trochas
fbff98f1dd merge 2025-10-24 23:22:01 +02:00
trochas
1354457836 Merge remote-tracking branch 'refs/remotes/origin/tp_rest' into tp_rest 2025-10-24 23:17:04 +02:00
Vu Tuan Minh
6388783cff Merge remote-tracking branch 'origin/tp_rest' into tp_rest
# Conflicts:
#	src/main/java/fr/istic/taa/jaxrs/DAO/QuestionDAO.java
#	src/main/java/fr/istic/taa/jaxrs/DAO/UtilisateurDAO.java
2025-10-24 23:14:33 +02:00
trochas
74b13cc7f4 correction Question 2025-10-24 23:13:02 +02:00
Vu Tuan Minh
d55e48b7e4 change the transaction in DAO, in case it's in transaction and block all, add exception 2025-10-24 23:11:47 +02:00
trochas
50c9f5a61e gitignore 2025-10-24 19:44:52 +02:00
trochas
4b2604fe17 corrections 2025-10-24 19:39:02 +02:00
Vu Tuan Minh
0553fc13f7 Minor fix README.md 2025-10-13 00:22:03 +02:00
Vu Tuan Minh
9b6b8d6c80 Finished and tested 2025-10-13 00:17:51 +02:00
tuanvu
b446b16027 push swagger 2025-10-06 09:56:36 +02:00
trochas
16126c6364 correction de resouce Question + Readme 2025-10-02 16:15:49 +02:00
trochas
9945ca0baa Question Resource + merge 2025-10-02 15:48:41 +02:00
trochas
23363c5070 Question Resource 2025-10-02 15:47:51 +02:00
tuanvu
de18627c30 push quizz 2025-10-02 15:45:01 +02:00
tuanvu
d79f4f4b8f push quizz 2025-10-02 14:39:53 +02:00
tuanvu
27aff73824 update session resource 2025-10-02 14:03:25 +02:00
trochas
b8df2eccd8 git ignore 2025-10-02 13:29:28 +02:00
tuanvu
a99c7676b1 updated resource 2025-10-01 16:15:56 +02:00
Vu Tuan Minh
bd301f169d FIX XML
ADD MAPPING
2025-10-01 13:32:45 +02:00
Vu Tuan Minh
f4c990c0fb Tested methods Utilisateur
/register
/login
/
/{id}
/register

add Session
2025-10-01 01:34:47 +02:00
Vu Tuan Minh
72ecb82a46 Tested methods Utilisateur
/register
/login
/
/{id}
2025-09-30 23:19:48 +02:00
Vu Tuan Minh
d18bb21b74 REST API works 2025-09-30 01:49:48 +02:00
40 changed files with 2089 additions and 4 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/target/
/data/

112
README.md
View File

@@ -1,13 +1,117 @@
# TP TAA
## TP1 - TP2
## TP2 Partie API REST
### Compte rendu
Le compte rendu pour TP1 et TP2 la partie servlet sont dans la branche tp_servlet
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 lAPI.
- DTO (Data Transfer Objects) afin déviter les problèmes de boucle infinie liés aux relations bidirectionnelles entre entités.
Le compte rendu pour TP2 la partie API Rest est dans la branche tp_rest
#### 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 lancement du serveur, on a testé des appels API avec Insomnia et l'interface Swagger est accessible via: http://localhost:8080/api
Seuls Utilisateur et Question sont fonctionnels, il reste quelques problèmes sur le reste.
#### 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&lt;Session&gt;
-email : String
-password : String
-quizzs : String
}
class Session{
-id : int
-codePIN : int
-quizzs : List&lt;Quizz&gt;
-utilisateurs : List&lt;Utilisateur&gt;
-theme : String
}
class Quizz{
-session: Session
-id : int
-createur: Utilisateur
}
class Reponse{
-id : int
-question: Question
-reponses : ArrayList&lt;String&gt;
}
class Choix{
-choix : ArrayList&lt;String&gt;
}
class ReponseCourte{
}
class Question{
-id : int
-enonce : String
-reponse: Reponse
}
```
##### Utilisateur Resource
| Methode | URL | Description |
|---------|---------------------------------------------------|-------------------------------------------------------|
| GET | `/utilisateur` | Retourne toute la liste de l'utilisateur |
| POST | `/utilisateur/register` | S'incrire nouveaux utilisateur |
| GET | `/utilisateur/login` | Se connecter |
| GET | `/utilisateur/{id}` | Retourne l'utilisateur par id |
| GET | `/utilisateur/{user_id}/session` | Retoune la liste de session que l'utilisateur attends |
| 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
| Methode | URL | Description |
|---------|--------------------------------------|------------------------------------|
| GET | `/session` | Retourne toute la liste du session |
| GET | `/session/{id}` | Retourne la session par id |
| GET | `/session/{session_id}/quizzs` | Retourne la liste de quizzs |
| 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}/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
| 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 |
| 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

129
pom.xml Normal file
View File

@@ -0,0 +1,129 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>fr.istic.taa</groupId>
<artifactId>jaxrs-example</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>jaxrs-example</name>
<url>http://maven.apache.org</url>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<org.mapstruct.version>1.6.3</org.mapstruct.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- ==== JPA / Hibernate ==== -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.2.7.Final</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.1.0</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.2</version>
</dependency>
<!-- ==== REST API ==== -->
<!--https://mvnrepository.com/artifact/org.jboss.resteasy/resteasy-undertow -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-undertow</artifactId>
<version>6.2.4.Final</version>
</dependency>
<!--https://mvnrepository.com/artifact/org.jboss.resteasy/resteasy-jackson2-provider -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>6.2.4.Final</version>
</dependency>
<!--https://mvnrepository.com/artifact/org.jboss.resteasy/resteasy-jaxb-provider -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>6.2.4.Final</version>
</dependency>
<!-- OpenAPI-->
<!--https://mvnrepository.com/artifact/io.swagger.core.v3/swagger-jaxrs2-jakarta -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-jakarta</artifactId>
<version>2.2.15</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-servlet-initializer-v2</artifactId>
<version>2.2.15</version>
</dependency>
<!-- ==== MapStruct/ Lombok ==== -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.42</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>11</source>
<target>11</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.42</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.6.3</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>

3
run-hsqldb-server.bat Executable file
View File

@@ -0,0 +1,3 @@
mkdir data
cd data
java -cp ..\hsqldb-2.7.2.jar org.hsqldb.Server

4
run-hsqldb-server.sh Executable file
View File

@@ -0,0 +1,4 @@
mvn dependency:copy-dependencies
mkdir data 2> /dev/null
cd data
java -cp ../target/dependency/hsqldb-2.7.2.jar org.hsqldb.Server

2
show-hsqldb.bat Normal file
View File

@@ -0,0 +1,2 @@
java -cp ./target/dependency/hsqldb-2.7.2.jar org.hsqldb.util.DatabaseManagerSwing --driver org.hsqldb.jdbcDriver --url jdbc:hsqldb:hsql://localhost/ --user SA

2
show-hsqldb.sh Executable file
View File

@@ -0,0 +1,2 @@
#mvn dependency:copy-dependencies
java -cp ./target/dependency/hsqldb-2.7.2.jar org.hsqldb.util.DatabaseManagerSwing --driver org.hsqldb.jdbcDriver --url jdbc:hsqldb:hsql://localhost/ --user SA

View File

@@ -0,0 +1,60 @@
package fr.istic.taa.jaxrs.DAO;
import java.io.Serializable;
import java.util.List;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityTransaction;
public abstract class AbstractJpaDao<K, T extends Serializable> implements IGenericDao<K, T> {
protected EntityManager em;
private Class<T> entityClass;
public AbstractJpaDao() {
this.em = EntityManagerHelper.getEntityManager();
}
//Pour connaitre qu'on travaille avec quelle classe
public void setClass(Class<T> class2){
this.entityClass= class2;
}
public T findOne(K id) {
return em.find(entityClass, id);
}
public List<T> findAll() {
return em.createQuery("select e from " + entityClass.getName() + " as e", entityClass).getResultList();
}
//CRUD
public void create(T entity){
EntityTransaction t = em.getTransaction();
t.begin();
em.persist(entity);
t.commit();
}
public T findById(K id) {
return em.find(entityClass, id);
}
public T update(T entity){
EntityTransaction t = em.getTransaction();
t.begin();
T T_sync = em.merge(entity);
t.commit();
return T_sync;
}
public void delete(T entity){
EntityTransaction t = em.getTransaction();
t.begin();
em.remove(entity);
t.commit();
}
public void deleteById(K id) {
T T_to_be_deleted = em.find(entityClass, id);
em.remove(T_to_be_deleted);
}
}

View File

@@ -0,0 +1,57 @@
package fr.istic.taa.jaxrs.DAO;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
public class EntityManagerHelper {
private static EntityManagerFactory emf;
private static final ThreadLocal<EntityManager> threadLocal;
static {
try {
emf = Persistence.createEntityManagerFactory("dev");
} catch (Exception e) {
emf = null;
e.printStackTrace();
}
threadLocal = new ThreadLocal<EntityManager>();
}
public static EntityManager getEntityManager() {
EntityManager em = threadLocal.get();
if (em == null) {
em = emf.createEntityManager();
threadLocal.set(em);
}
return em;
}
public static void closeEntityManager() {
EntityManager em = threadLocal.get();
if (em != null) {
em.close();
threadLocal.set(null);
}
}
public static void closeEntityManagerFactory() {
emf.close();
}
public static void beginTransaction() {
getEntityManager().getTransaction().begin();
}
public static void rollback() {
getEntityManager().getTransaction().rollback();
}
public static void commit() {
getEntityManager().getTransaction().commit();
}
}

View File

@@ -0,0 +1,14 @@
package fr.istic.taa.jaxrs.DAO;
import java.io.Serializable;
import java.util.List;
public interface IGenericDao<K, T extends Serializable> {
void create(final T entity);
T findById(final K id);
T update(final T entity);
void delete(final T entity);
void deleteById(final K id);
List<T> findAll();
T findOne(final K id);
}

View File

@@ -0,0 +1,108 @@
package fr.istic.taa.jaxrs.DAO;
import java.util.ArrayList;
import java.util.List;
import fr.istic.taa.jaxrs.metier.Choix;
import fr.istic.taa.jaxrs.metier.Question;
import fr.istic.taa.jaxrs.metier.Reponse;
import fr.istic.taa.jaxrs.metier.ReponseCourte;
import fr.istic.taa.jaxrs.metier.Session;
import fr.istic.taa.jaxrs.metier.Utilisateur;
import jakarta.persistence.EntityTransaction;
public class QuestionDAO extends AbstractJpaDao<Integer, Question> {
public QuestionDAO() {
super();
this.setClass(Question.class);
}
public void addReponse(String reponse, int questionId) {
EntityTransaction t = em.getTransaction();
try {
if (!t.isActive()) t.begin();
Question q = em.find(Question.class, questionId);
if (q != null && q.getReponse() != null) {
q.getReponse().getReponses().add(reponse);
em.merge(q);
}
t.commit();
} catch (Exception e) {
if (t.isActive()) t.rollback();
e.printStackTrace();
}
}
public void deleteReponse(int questionId) {
EntityTransaction t = em.getTransaction();
try {
if (!t.isActive()) t.begin();
Question q = em.find(Question.class, questionId);
if (q != null && q.getReponse() != null) {
q.getReponse().setReponses(new ArrayList<>());
em.merge(q);
}
t.commit();
} catch (Exception e) {
if (t.isActive()) t.rollback();
e.printStackTrace();
}
}
public void setReponse(Reponse rep, Integer id) {
EntityTransaction t = em.getTransaction();
try {
if (!t.isActive()) t.begin();
Question q = em.find(Question.class, id);
if (q != null) {
q.setReponse(rep);
em.merge(q);
}
t.commit();
} catch (Exception e) {
if (t.isActive()) t.rollback();
e.printStackTrace();
}
}
public void setChoix(Integer id) {
setReponse(new Choix(), id);
}
public void setReponseCourte(Integer id) {
setReponse(new ReponseCourte(), id);
}
public void addChoix(Integer id, String choix) {
EntityTransaction t = em.getTransaction();
try {
if (!t.isActive()) t.begin();
Question q = em.find(Question.class, id);
if (q != null && q.getReponse() instanceof Choix) {
((Choix) q.getReponse()).getChoix().add(choix);
em.merge(q);
}
t.commit();
} catch (Exception e) {
if (t.isActive()) t.rollback();
e.printStackTrace();
}
}
public void changeEnonce(String newEnonce, Integer id) {
EntityTransaction t = em.getTransaction();
try {
if (!t.isActive()) t.begin();
Question q = em.find(Question.class, id);
if (q != null) {
q.setEnonce(newEnonce);
em.merge(q);
}
t.commit();
} catch (Exception e) {
if (t.isActive()) t.rollback();
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,48 @@
package fr.istic.taa.jaxrs.DAO;
import fr.istic.taa.jaxrs.metier.Question;
import jakarta.persistence.EntityTransaction;
import fr.istic.taa.jaxrs.metier.Quizz;
public class QuizzDAO extends AbstractJpaDao<Integer, Quizz> {
public QuizzDAO(){
super();
this.setClass(Quizz.class);
}
public void deleteAllQuestion(int quizzId) {
EntityTransaction t = em.getTransaction();
try {
if (!t.isActive()) t.begin();
Quizz quizz = em.find(Quizz.class, quizzId);
if (quizz != null) {
quizz.getQuestions().clear();
em.merge(quizz);
}
t.commit();
} catch (Exception e) {
if (t.isActive()) t.rollback();
e.printStackTrace();
throw e;
}
}
public void addQuestion(int quizzId, int questionId) {
EntityTransaction t = em.getTransaction();
try {
if (!t.isActive()) t.begin();
Quizz quizz = em.find(Quizz.class, quizzId);
Question question = em.find(Question.class, questionId);
if (quizz != null && question != null) {
quizz.getQuestions().add(question);
em.merge(quizz);
}
t.commit();
} catch (Exception e) {
if (t.isActive()) t.rollback();
e.printStackTrace();
throw e;
}
}
}

View File

@@ -0,0 +1,11 @@
package fr.istic.taa.jaxrs.DAO;
import fr.istic.taa.jaxrs.metier.Reponse;
public class ReponseDAO extends AbstractJpaDao<Integer, Reponse> {
public ReponseDAO(){
super();
this.setClass(Reponse.class);
}
}

View File

@@ -0,0 +1,33 @@
package fr.istic.taa.jaxrs.DAO;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Query;
import fr.istic.taa.jaxrs.metier.Session;
import java.util.List;
public class SessionDAO extends AbstractJpaDao<Integer, Session> {
public SessionDAO() {
super();
this.setClass(Session.class);
}
public List<Session> findByTheme(String theme) {
EntityTransaction t = em.getTransaction();
try {
if (!t.isActive()) {
t.begin();
}
Query query = em.createQuery("SELECT s FROM Session s WHERE s.theme = :theme", Session.class);
query.setParameter("theme", theme);
List<Session> sessions = query.getResultList();
t.commit();
return sessions;
} catch (Exception e) {
if (t.isActive()) t.rollback();
e.printStackTrace();
throw e;
}
}
}

View File

@@ -0,0 +1,61 @@
package fr.istic.taa.jaxrs.DAO;
import jakarta.persistence.EntityTransaction;
import fr.istic.taa.jaxrs.metier.Session;
import fr.istic.taa.jaxrs.metier.Utilisateur;
import java.util.List;
public class UtilisateurDAO extends AbstractJpaDao<Integer, Utilisateur> {
public UtilisateurDAO() {
super();
this.setClass(Utilisateur.class);
}
public Utilisateur findByEmail(String email) {
List<Utilisateur> results = em.createQuery(
"SELECT u FROM Utilisateur u WHERE u.email = :email", Utilisateur.class)
.setParameter("email", email)
.getResultList();
return results.isEmpty() ? null : results.get(0);
}
public void addToSession(int userId, int sessionId) {
EntityTransaction t = em.getTransaction();
try {
if (!t.isActive()) {
t.begin();
}
Session s = em.find(Session.class, sessionId);
Utilisateur u = em.find(Utilisateur.class, userId);
if (s == null || u == null) {
throw new IllegalArgumentException("User or session not found");
}
if (!u.getSessions().contains(s)) {
u.getSessions().add(s);
em.merge(u);
}
t.commit();
} catch (Exception e) {
if (t.isActive()) t.rollback();
e.printStackTrace();
throw e;
}
}
@Override
public void create(Utilisateur entity) {
EntityTransaction t = em.getTransaction();
try {
if (!t.isActive()) {
t.begin();
}
em.merge(entity);
t.commit();
} catch (Exception e) {
if (t.isActive()) t.rollback();
e.printStackTrace();
throw e;
}
}
}

View File

@@ -0,0 +1,13 @@
package fr.istic.taa.jaxrs.DTO;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Data;
import java.util.List;
@Data
@XmlRootElement
public class QuestionDTO {
private int id;
private String enonce;
}

View File

@@ -0,0 +1,14 @@
package fr.istic.taa.jaxrs.DTO;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Data;
import java.util.List;
@Data
@XmlRootElement
public class QuizzDTO {
private int id;
private List<Integer> sessionsId;
private Integer utilisateurId;
private List<Integer> questionsId;
}

View File

@@ -0,0 +1,11 @@
package fr.istic.taa.jaxrs.DTO;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Data;
import java.util.List;
@Data
@XmlRootElement
public class ReponseDTO {
public List<String> reponses;
}

View File

@@ -0,0 +1,17 @@
package fr.istic.taa.jaxrs.DTO;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Data;
import java.util.List;
@Data
@XmlRootElement
public class SessionDTO {
private String theme;
private int codePIN;
private int id;
private List<Integer> quizzsId;
private List<Integer> utilisateursId;
}

View File

@@ -0,0 +1,13 @@
package fr.istic.taa.jaxrs.DTO;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Data;
@Data
@XmlRootElement
public class UtilisateurDTO {
private int id;
private String name;
private String email;
private String password;
}

View File

@@ -0,0 +1,26 @@
package fr.istic.taa.jaxrs.Mapper;
import fr.istic.taa.jaxrs.DTO.QuestionDTO;
import fr.istic.taa.jaxrs.metier.Question;
import fr.istic.taa.jaxrs.metier.Reponse;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface QuestionMapper {
QuestionMapper INSTANCE = Mappers.getMapper( QuestionMapper.class );
QuestionDTO toDTO(Question question);
Question toEntity(QuestionDTO questionDTO);
List<QuestionDTO> toDTOs(List<Question> questionList);
default List<String> question_ReponseString(Reponse reponse){
return reponse.getReponses();
}
}

View File

@@ -0,0 +1,43 @@
package fr.istic.taa.jaxrs.Mapper;
import fr.istic.taa.jaxrs.DTO.QuizzDTO;
import fr.istic.taa.jaxrs.metier.Question;
import fr.istic.taa.jaxrs.metier.Session;
import fr.istic.taa.jaxrs.metier.Quizz;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.ArrayList;
import java.util.List;
@Mapper
public interface QuizzMapper {
QuizzMapper INSTANCE = Mappers.getMapper( QuizzMapper.class );
//https://www.baeldung.com/mapstruct-map-source-object-target-list
//https://mapstruct.org/
@Mapping(target="sessionsId", expression="java(function_mapS(quizz.getSessions()))")
@Mapping(target="utilisateurId", source = "createur.id")
@Mapping(target="questionsId",expression="java(function_mapQ(quizz.getQuestions()))")
QuizzDTO toDTO(Quizz quizz);
Quizz toEntity(QuizzDTO quizzDTO);
List<QuizzDTO> toDTOs(List<Quizz> quizzes);
default List<Integer> function_mapQ(List<Question> questionList){
List<Integer> list=new ArrayList<Integer>();
for(Question question : questionList){
list.add(question.getId());
}
return list;
}
default List<Integer> function_mapS(List<Session> sessionList){
List<Integer> list=new ArrayList<Integer>();
for(Session session : sessionList){
list.add(session.getId());
}
return list;
}
}

View File

@@ -0,0 +1,42 @@
package fr.istic.taa.jaxrs.Mapper;
import fr.istic.taa.jaxrs.DTO.SessionDTO;
import fr.istic.taa.jaxrs.metier.Quizz;
import fr.istic.taa.jaxrs.metier.Session;
import fr.istic.taa.jaxrs.metier.Utilisateur;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.ArrayList;
import java.util.List;
@Mapper
public interface SessionMapper {
SessionMapper INSTANCE = Mappers.getMapper( SessionMapper.class );
@Mapping(target="utilisateursId",expression="java(function_mapU(session.getUtilisateurs()))")
@Mapping(target="quizzsId",expression="java(function_mapQ(session.getQuizzs()))")
SessionDTO toDTO(Session session);
Session toEntity(SessionDTO sessionDTO);
List<SessionDTO> toDTOs(List<Session> sessions);
//https://www.baeldung.com/mapstruct-map-source-object-target-list
default List<Integer> function_mapU(List<Utilisateur> utilisateurList) {
List<Integer> result = new ArrayList<>();
for (Utilisateur utilisateur : utilisateurList) {
result.add(utilisateur.getId());
}
return result;
}
default List<Integer> function_mapQ(List<Quizz> quizzList) {
List<Integer> result = new ArrayList<>();
for (Quizz quizz : quizzList) {
result.add(quizz.getId());
}
return result;
}
}

View File

@@ -0,0 +1,19 @@
package fr.istic.taa.jaxrs.Mapper;
import fr.istic.taa.jaxrs.DTO.UtilisateurDTO;
import fr.istic.taa.jaxrs.metier.Utilisateur;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface UtilisateurMapper {
UtilisateurMapper INSTANCE = Mappers.getMapper(UtilisateurMapper.class);
UtilisateurDTO toDTO(Utilisateur utilisateur);
Utilisateur toEntity(UtilisateurDTO dto);
List<UtilisateurDTO> toDTOs(List<Utilisateur> utilisateurList);
}

View File

@@ -0,0 +1,28 @@
package fr.istic.taa.jaxrs;
import io.undertow.Undertow;
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
import java.util.logging.Logger;
/**
* RESTfull microservice, based on JAX-RS and JBoss Undertow
*
*/
public class RestServer {
private static final Logger logger = Logger.getLogger(RestServer.class.getName());
public static void main(String[] args) {
UndertowJaxrsServer ut = new UndertowJaxrsServer();
TestApplication ta = new TestApplication();
ut.deploy(ta);
ut.start(
Undertow.builder()
.addHttpListener(8080, "localhost")
);
logger.info("JAX-RS based micro-service running!");
}
}

View File

@@ -0,0 +1,45 @@
/**
* JBoss, Home of Professional Open Source
* Copyright Red Hat, Inc., and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.istic.taa.jaxrs;
import java.util.HashSet;
import java.util.Set;
import fr.istic.taa.jaxrs.rest.*;
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("/")
public class TestApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> clazzes = new HashSet<Class<?>>();
clazzes.add(OpenApiResource.class);
clazzes.add(QuestionResource.class);
clazzes.add(QuizzResource.class);
clazzes.add(UtilisateurResource.class);
clazzes.add(SessionResource.class);
clazzes.add(SwaggerResource.class);
return clazzes;
}
}

View File

@@ -0,0 +1,28 @@
package fr.istic.taa.jaxrs.metier;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.PrimaryKeyJoinColumn;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Getter
@Setter
@NoArgsConstructor
@PrimaryKeyJoinColumn(name = "Choix_Id")
public class Choix extends Reponse{
List<String> choix;
@Override
public String valHTML(){
String res = "";
for (String val : this.choix) {
res+=val+"<br/>";
}
return res;
}
}

View File

@@ -0,0 +1,29 @@
package fr.istic.taa.jaxrs.metier;
import jakarta.persistence.*;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.io.Serializable;
@Entity
@Getter
@Setter
@NoArgsConstructor
@XmlRootElement
public class Question implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String enonce;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name ="bonne_reponse", referencedColumnName = "id")
private Reponse reponse;
@ManyToOne
@JoinColumn(name="id_quizz")
private Quizz quizz;
}

View File

@@ -0,0 +1,32 @@
package fr.istic.taa.jaxrs.metier;
import jakarta.persistence.*;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Setter
@NoArgsConstructor
@XmlRootElement
public class Quizz implements Serializable {
@Id
@GeneratedValue
private int id;
@ManyToMany
private List<Session> sessions;
@ManyToOne
@JoinColumn(name="id_utilisateur")
private Utilisateur createur;
@OneToMany(mappedBy = "quizz")
private List<Question> questions=new ArrayList<Question>();
}

View File

@@ -0,0 +1,31 @@
package fr.istic.taa.jaxrs.metier;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Getter
@Setter
@NoArgsConstructor
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="Type_reponse")
@DiscriminatorValue("Reponse")
public abstract class Reponse implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@OneToOne
private Question question;
public List<String> reponses = new ArrayList<String>();
public String valHTML(){
return "";
}
}

View File

@@ -0,0 +1,21 @@
package fr.istic.taa.jaxrs.metier;
import jakarta.persistence.Entity;
import jakarta.persistence.PrimaryKeyJoinColumn;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Getter
@Setter
@NoArgsConstructor
@PrimaryKeyJoinColumn(name = "RC_Id")
public class ReponseCourte extends Reponse{
String value;
@Override
public String valHTML(){
return "INPUT";
}
}

View File

@@ -0,0 +1,32 @@
package fr.istic.taa.jaxrs.metier;
import java.io.Serializable;
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;
@Entity
@Getter
@Setter
@NoArgsConstructor
@XmlRootElement
public class Session implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(unique=true)
private int codePIN;
@ManyToMany(mappedBy="sessions", fetch = FetchType.EAGER)
private List<Quizz> quizzs = new ArrayList<>();
@ManyToMany(mappedBy = "sessions", fetch = FetchType.EAGER)
private List<Utilisateur> utilisateurs = new ArrayList<>();
private String theme;
}

View File

@@ -0,0 +1,35 @@
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;
import java.util.List;
@Entity
@Getter
@Setter
@NoArgsConstructor
@XmlRootElement
public class Utilisateur implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@Column(unique=true)
private String email;
private String password;
@ManyToMany
@JoinTable(
name="utilisateur_session",
joinColumns = @JoinColumn(name="utilisateur_id"),
inverseJoinColumns = @JoinColumn(name = "session_id")
)
private List<Session> sessions= new ArrayList<Session>();
@OneToMany(mappedBy = "createur")
private List<Quizz> quizzs = new ArrayList<Quizz>();;
}

View File

@@ -0,0 +1,222 @@
package fr.istic.taa.jaxrs.rest;
import java.util.List;
import fr.istic.taa.jaxrs.DAO.QuestionDAO;
import fr.istic.taa.jaxrs.DTO.QuestionDTO;
import fr.istic.taa.jaxrs.Mapper.QuestionMapper;
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.PUT;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
@Path("question")
@Consumes({"application/json", "application/xml"})
@Produces({"application/json", "application/xml"})
public class QuestionResource {
private final QuestionDAO questionDAO = new QuestionDAO();
private final QuestionMapper mapper = QuestionMapper.INSTANCE;
@GET
@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<QuestionDTO> listQuestion() {
List<Question> questions = questionDAO.findAll();
return mapper.toDTOs(questions);
}
@POST
@Path("/addQuestion")
@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.getEnonce() + "\"").build();
}
@PUT
@Path("/{question_id}/changeQuestion/")
@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) {
return Response.status(Response.Status.NOT_FOUND).build();
}
questionDAO.changeEnonce(newQuestion, id);
return Response.status(Response.Status.CREATED).entity("Enonce de la question mis à jour : " + newQuestion).build();
}
@GET
@Path("/{question_id}/getReponse/")
@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) {
return Response.status(Response.Status.NOT_FOUND).build();
}
Reponse result = question.getReponse();
return Response.ok(result).build();
}
@PUT
@Path("/{question_id}/addReponse/")
@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) {
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();
}
@DELETE
@Path("/{question_id}/deletReponses/")
@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) {
return Response.status(Response.Status.NOT_FOUND).build();
}
questionDAO.deleteReponse(id);
return Response.status(Response.Status.CREATED).entity("Reponses supprimé de la question " + id).build();
}
@PUT
@Path("/{question_id}/setReponse/choix")
@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) {
return Response.status(Response.Status.NOT_FOUND).build();
}
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")
@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) {
return Response.status(Response.Status.NOT_FOUND).build();
}
questionDAO.setReponseCourte(id);
return Response.status(Response.Status.CREATED).entity("Reponses courte mise sur la question " + id).build();
}
@PUT
@Path("/{question_id}/AddChoix")
@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) {
return Response.status(Response.Status.NOT_FOUND).build();
}
if (question.getReponse() instanceof Choix) {
return Response.status(Response.Status.EXPECTATION_FAILED).build();
}
questionDAO.addChoix(id, choix);
return Response.status(Response.Status.CREATED).entity("Reponses courte mise sur la question " + id).build();
}
}

View File

@@ -0,0 +1,180 @@
package fr.istic.taa.jaxrs.rest;
import java.util.List;
import fr.istic.taa.jaxrs.DAO.QuestionDAO;
import fr.istic.taa.jaxrs.DAO.QuizzDAO;
import fr.istic.taa.jaxrs.DTO.QuestionDTO;
import fr.istic.taa.jaxrs.DTO.QuizzDTO;
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.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
@Path("quizz")
@Consumes({"application/json", "application/xml"})
@Produces({"application/json", "application/xml"})
public class QuizzResource {
public final QuizzMapper mapper = QuizzMapper.INSTANCE;
public final QuizzDAO quizzDAO = new QuizzDAO();
public final QuestionDAO questionDAO = new QuestionDAO();
@POST
@Path("/addQuizz")
@Operation(summary = "Create a new quizz",
tags = {"Quizz"},
description = "Create a new quizz",
responses = {@ApiResponse(responseCode = "201", description = "Quizz added.")}
)
public Response addQuizz(
@Parameter(description = "The quizz details to be added", required = true) QuizzDTO dto) {
Quizz quizz = mapper.toEntity(dto);
quizzDAO.create(quizz);
return Response.status(Response.Status.CREATED).entity("Quizz ajouté avec Succès : \"" + dto.getId() + "\"").build();
}
@GET
@Operation(summary = "List all quizz",
tags = {"Quizz"},
description = "List all quizz.",
responses = {
@ApiResponse(description = "List of all quizz", content = @Content(
array = @ArraySchema(schema = @Schema(implementation = Question.class))))
}
)
public List<QuizzDTO> listQuizz() {
List<Quizz> quizz = quizzDAO.findAll();
return mapper.toDTOs(quizz);
}
@GET
@Path("/{quizz_id}")
@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) {
return Response.status(Response.Status.NOT_FOUND).build();
}
QuizzDTO dto = mapper.toDTO(quizz);
return Response.status(Response.Status.OK).entity(dto).build();
}
@GET
@Path("/{quizz_id}/questions")
@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<Question> questionList = quizz.getQuestions();
List<QuestionDTO> dtos = QuestionMapper.INSTANCE.toDTOs(questionList);
return Response.status(Response.Status.OK).entity(dtos).build();
}
@PUT
@Path("/{quizz_id}/add_question/{question_id}")
@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.update(quizz);
QuizzDTO quizzDTO = mapper.toDTO(quizz);
return Response.status(Response.Status.OK).entity(quizzDTO).build();
}
@PUT
@Path("/{quizz_id}/deleteQ")
@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.deleteAllQuestion(quizzId);
quizzDAO.update(quizz);
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();
}
}

View File

@@ -0,0 +1,158 @@
package fr.istic.taa.jaxrs.rest;
import java.util.List;
import fr.istic.taa.jaxrs.DAO.SessionDAO;
import fr.istic.taa.jaxrs.DTO.QuizzDTO;
import fr.istic.taa.jaxrs.DTO.SessionDTO;
import fr.istic.taa.jaxrs.DTO.UtilisateurDTO;
import fr.istic.taa.jaxrs.Mapper.QuizzMapper;
import fr.istic.taa.jaxrs.Mapper.SessionMapper;
import fr.istic.taa.jaxrs.Mapper.UtilisateurMapper;
import fr.istic.taa.jaxrs.metier.Quizz;
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.Consumes;
import jakarta.ws.rs.DELETE;
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("session")
@Consumes({"application/json", "application/xml"})
@Produces({"application/json", "application/xml"})
public class SessionResource {
private final SessionDAO sessionDAO = new SessionDAO();
private final SessionMapper mapper = SessionMapper.INSTANCE;
@POST
@Path("/addSession")
@Operation(summary = "Create a new session",
tags = {"Session"},
description = "Create a new session",
responses = {@ApiResponse(responseCode = "201", description = "Session added.")}
)
public Response addQuizz(
@Parameter(description = "The session details to be added", required = true) SessionDTO dto) {
Session session = mapper.toEntity(dto);
sessionDAO.create(session);
return Response.status(Response.Status.CREATED).entity("Session ajouté avec Succès : \"" + dto.getTheme() + "\"").build();
}
@GET
@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<SessionDTO> listSession() {
List<Session> sessions = sessionDAO.findAll();
return mapper.toDTOs(sessions);
}
@GET
@Path("/{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();
}
SessionDTO result = mapper.toDTO(session);
return Response.status(Response.Status.OK).entity(result).build();
}
@GET
@Path("/{session_id}/quizzs")
@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<QuizzDTO> results = QuizzMapper.INSTANCE.toDTOs(session.getQuizzs());
return Response.status(Response.Status.OK).entity(results).build();
}
@GET
@Path("/{session_id}/utilisateurs")
@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<Utilisateur> utilisateurList = session.getUtilisateurs();
List<UtilisateurDTO> dtos = UtilisateurMapper.INSTANCE.toDTOs(utilisateurList);
return Response.status(Response.Status.OK).entity(dtos).build();
}
@DELETE
@Path("/{session_id}/delete")
@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("Session deleted").build();
}
}

View File

@@ -0,0 +1,45 @@
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;
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 Response Get1() {
return getFileContent("index.html");
}
@GET
@Path("{path:.*}")
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
}
}
}

View File

@@ -0,0 +1,204 @@
package fr.istic.taa.jaxrs.rest;
import fr.istic.taa.jaxrs.DAO.SessionDAO;
import fr.istic.taa.jaxrs.DAO.UtilisateurDAO;
import fr.istic.taa.jaxrs.DTO.SessionDTO;
import fr.istic.taa.jaxrs.DTO.UtilisateurDTO;
import fr.istic.taa.jaxrs.Mapper.SessionMapper;
import fr.istic.taa.jaxrs.Mapper.UtilisateurMapper;
import fr.istic.taa.jaxrs.metier.Question;
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.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;
import java.util.List;
@Path("utilisateur")
@Consumes({"application/json", "application/xml"})
@Produces({"application/json", "application/xml"})
public class UtilisateurResource {
private final UtilisateurDAO utilisateurDAO = new UtilisateurDAO();
private final UtilisateurMapper mapper = UtilisateurMapper.INSTANCE;
@GET
@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<UtilisateurDTO> listUtilisateur() {
List<Utilisateur> utilisateurs = utilisateurDAO.findAll();
return mapper.toDTOs(utilisateurs);
}
//https://stackoverflow.com/questions/9269040/which-http-response-code-for-this-email-is-already-registered
@POST
@Path("/register")
@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);
//VERIFACTION S'IL EXISTE DANS BDD
if (existing != null) {
return Response.status(Response.Status.CONFLICT).entity("Email est déjà registré").build();
}
Utilisateur utilisateur = mapper.toEntity(dto);
utilisateurDAO.create(utilisateur);
return Response.status(Response.Status.CREATED).entity("Registration succès").build();
}
@POST
@Path("/login")
@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) {
return Response.status(Response.Status.NOT_FOUND).entity("Email n'existe pas").build();
} else if (!utilisateur.getPassword().equals(dto.getPassword())) {
return Response.status(Response.Status.UNAUTHORIZED).entity("Mauvais mdp").build();
}
UtilisateurDTO result = mapper.toDTO(utilisateur);
return Response.status(Response.Status.OK).entity(result).build();
}
@GET
@Path("/{id}")
@Operation(summary = "Get user by ID",
tags = {"Utilisateurs"},
description = "Get User by ID",
responses = {
@ApiResponse(description = "Utilisateur", content = @Content(
schema = @Schema(implementation = Utilisateur.class)
)),
@ApiResponse(responseCode = "400", description = "Invalid ID supplied"),
@ApiResponse(responseCode = "404", description = "User not found")
})
public Response getUtilisateur(@Parameter(
description = "ID of user that needs to be fetched",
schema = @Schema(
type = "integer",
format = "int32",
description = "param ID of user that needs to be fetched"
),
required = true)
@PathParam("id") Integer id) {
Utilisateur utilisateur = utilisateurDAO.findById(id);
if (utilisateur == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
UtilisateurDTO result = mapper.toDTO(utilisateur);
return Response.ok(result).build();
}
@PUT
@Path("/{user_id}/add_session/{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);
Utilisateur utilisateur = utilisateurDAO.findById(user_id);
if (existingSession == null || utilisateur == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
//If user already joined
if (utilisateur.getSessions().contains(existingSession)) {
return Response.status(Response.Status.CONFLICT).build();
}
utilisateurDAO.addToSession(user_id, session_id);
utilisateurDAO.update(utilisateur);
// We update it so have to return new DTO
UtilisateurDTO dto = mapper.toDTO(utilisateur);
return Response.status(Response.Status.OK).entity(dto).build();
}
@GET
@Path("{user_id}/session")
@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();
}
List<Session> sess = utilisateur.getSessions();
List<SessionDTO> dtos = SessionMapper.INSTANCE.toDTOs(sess);
return Response.status(Response.Status.OK).entity(dtos).build();
}
@DELETE
@Path("{user_id}/delete")
@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();
}
utilisateurDAO.delete(existing);
return Response.status(Response.Status.OK).build();
}
}

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="3.0">
<persistence-unit name="dev" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="jakarta.persistence.jdbc.driver"
value="org.hsqldb.jdbcDriver" />
<property name="jakarta.persistence.jdbc.url"
value="jdbc:hsqldb:hsql://localhost/" />
<property name="jakarta.persistence.jdbc.user" value="sa" />
<property name="jakarta.persistence.jdbc.password" value="" />
<property
name="jakarta.persistence.schema-generation.database.action"
value="create" />
<property name="jakarta.persistence.dialect"
value="org.hibernate.dialect.HSQLDialect" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
<persistence-unit name="prod" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="jakarta.persistence.jdbc.driver"
value="org.hsqldb.jdbcDriver" />
<property name="jakarta.persistence.jdbc.url"
value="jdbc:hsqldb:hsql://localhost/" />
<property name="jakarta.persistence.jdbc.user" value="sa" />
<property name="jakarta.persistence.jdbc.password" value="" />
<property
name="jakarta.persistence.schema-generation.database.action"
value="update" />
<property name="jakarta.persistence.dialect"
value="org.hibernate.dialect.HSQLDialect" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
<persistence-unit name="mysql">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="jakarta.persistence.jdbc.driver"
value="com.mysql.cj.jdbc.Driver" />
<property name="jakarta.persistence.jdbc.url"
value="jdbc:mysql://localhost/mydatabase" />
<property name="jakarta.persistence.jdbc.user" value="tlc" />
<property name="jakarta.persistence.jdbc.password" value="tlc" />
<property
name="jakarta.persistence.schema-generation.database.action"
value="update" />
<property name="jakarta.persistence.dialect"
value="org.hibernate.dialect.MySQL8Dialect" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.c3p0.min_size" value="5" />
<property name="hibernate.c3p0.max_size" value="20" />
<property name="hibernate.c3p0.timeout" value="300" />
<property name="hibernate.c3p0.max_statements" value="50" />
<property name="hibernate.c3p0.idle_test_period" value="3000" />
</properties>
</persistence-unit>
</persistence>

View File

@@ -0,0 +1,92 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css"
href="https://unpkg.com/swagger-ui-dist/swagger-ui.css">
<style>
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin: 0;
background: #fafafa;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js"></script>
<script>
window.onload = function () {
// Build a system
const ui = SwaggerUIBundle({
url: "http://localhost:8080/openapi.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
}
</script>
</body>
</html>