diff --git a/pom.xml b/pom.xml
index 059225b..525fd91 100644
--- a/pom.xml
+++ b/pom.xml
@@ -115,6 +115,15 @@
jmimemagic
0.1.3
+
+
+ io.quarkus
+ quarkus-smallrye-openapi
+
+
+ io.quarkus
+ quarkus-swagger-ui
+
diff --git a/src/main/java/fr/titionfire/BlackPage.java b/src/main/java/fr/titionfire/BlackPage.java
deleted file mode 100644
index a77d835..0000000
--- a/src/main/java/fr/titionfire/BlackPage.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package fr.titionfire;
-
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.Response;
-
-@Path("/api")
-public class BlackPage {
-
- @GET
- @Produces(MediaType.TEXT_PLAIN)
- public Response get() {
- return Response.noContent().build();
- }
-
-}
diff --git a/src/main/java/fr/titionfire/PingPage.java b/src/main/java/fr/titionfire/PingPage.java
new file mode 100644
index 0000000..2acd198
--- /dev/null
+++ b/src/main/java/fr/titionfire/PingPage.java
@@ -0,0 +1,27 @@
+package fr.titionfire;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
+import org.eclipse.microprofile.openapi.annotations.tags.Tag;
+
+@Tag(name = "Ping API", description = "API pour tester la connectivité")
+@Path("/api")
+public class PingPage {
+
+ @Operation(summary = "Renvoie un message de réussite", description = "Cette méthode renvoie un message de réussite si la connexion est établie avec succès.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite")
+ })
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response get() {
+ return Response.ok().build();
+ }
+
+}
diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java
index 09f864e..3926f5c 100644
--- a/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java
+++ b/src/main/java/fr/titionfire/ffsaf/domain/service/AffiliationService.java
@@ -233,7 +233,7 @@ public class AffiliationService {
.call(m -> (m.getUserId() == null) ? keycloakService.initCompte(m.getId()) :
keycloakService.setClubGroupMembre(m, club))
.call(m -> Panache.withTransaction(() -> licenceRepository.persist(
- new LicenceModel(null, m, saison, false, true))));
+ new LicenceModel(null, m, saison, null, true))));
}
public Uni> accept(AffiliationRequestSaveForm form) {
@@ -336,6 +336,7 @@ public class AffiliationService {
public Uni setAffiliation(long id, int saison) {
return clubRepository.findById(id)
.onItem().ifNull().failWith(new DNotFoundException("Club non trouvé"))
+ .call(model -> Mutiny.fetch(model.getAffiliations()))
.invoke(Unchecked.consumer(club -> {
if (club.getAffiliations().stream().anyMatch(affiliation -> affiliation.getSaison() == saison)) {
throw new DBadRequestException("Affiliation déjà existante");
diff --git a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java
index 6e6448d..35a4492 100644
--- a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java
+++ b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationEndpoints.java
@@ -2,13 +2,8 @@ package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.AffiliationService;
import fr.titionfire.ffsaf.rest.data.SimpleAffiliation;
-import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation;
-import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliationResume;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
-import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
-import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm;
import fr.titionfire.ffsaf.utils.GroupeUtils;
-import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.oidc.IdToken;
import io.quarkus.security.identity.SecurityIdentity;
import io.smallrye.mutiny.Uni;
@@ -17,14 +12,17 @@ import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.Response;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
+import org.eclipse.microprofile.openapi.annotations.tags.Tag;
-import java.net.URISyntaxException;
import java.util.List;
import java.util.function.Consumer;
+@Tag(name = "Affiliation API", description = "API pour gérer les affiliations")
@Path("api/affiliation")
public class AffiliationEndpoints {
@@ -38,85 +36,20 @@ public class AffiliationEndpoints {
@Inject
SecurityIdentity securityIdentity;
- @ConfigProperty(name = "upload_dir")
- String media;
-
Consumer checkPerm = Unchecked.consumer(id -> {
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken))
throw new DForbiddenException();
});
- @GET
- @Path("/request")
- @RolesAllowed({"federation_admin"})
- @Produces(MediaType.APPLICATION_JSON)
- public Uni> getAllAffRequest() {
- return service.getAllReq().map(o -> o.stream().map(SimpleReqAffiliationResume::fromModel).toList());
- }
-
- @POST
- @Path("/request")
- @Produces(MediaType.TEXT_PLAIN)
- @Consumes(MediaType.MULTIPART_FORM_DATA)
- public Uni saveAffRequest(AffiliationRequestForm form) {
- return service.save(form);
- }
-
- @GET
- @Path("/request/{id}")
- @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
- @Produces(MediaType.APPLICATION_JSON)
- public Uni getAffRequest(@PathParam("id") long id) {
- return service.getRequest(id).invoke(Unchecked.consumer(o -> {
- if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin"))
- throw new DForbiddenException();
- })).invoke(o -> checkPerm.accept(o.getClub()));
- }
-
- @DELETE
- @Path("/request/{id}")
- @RolesAllowed({"federation_admin"})
- @Produces(MediaType.APPLICATION_JSON)
- public Uni> getDelAffRequest(@PathParam("id") long id) {
- return service.getRequest(id).invoke(Unchecked.consumer(o -> {
- if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin"))
- throw new DForbiddenException();
- })).invoke(o -> checkPerm.accept(o.getClub()))
- .chain(o -> service.deleteReqAffiliation(id));
- }
-
- @PUT
- @Path("/request/save")
- @RolesAllowed({"federation_admin"})
- @Produces(MediaType.TEXT_PLAIN)
- @Consumes(MediaType.MULTIPART_FORM_DATA)
- public Uni> saveAdminAffRequest(AffiliationRequestSaveForm form) {
- return service.saveAdmin(form);
- }
-
- @PUT
- @Path("/request/edit")
- @RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
- @Produces(MediaType.TEXT_PLAIN)
- @Consumes(MediaType.MULTIPART_FORM_DATA)
- public Uni> saveEditAffRequest(AffiliationRequestForm form) {
- return service.saveEdit(form);
- }
-
- @PUT
- @Path("/request/apply")
- @RolesAllowed({"federation_admin"})
- @Produces(MediaType.TEXT_PLAIN)
- @Consumes(MediaType.MULTIPART_FORM_DATA)
- public Uni> acceptAffRequest(AffiliationRequestSaveForm form) {
- return service.accept(form);
- }
-
-
@GET
@Path("/current")
@RolesAllowed({"federation_admin"})
@Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Renvoie les affiliations pour la saison en cours", description = "Cette méthode renvoie les affiliations pour la saison en cours. Seuls les administrateurs de la fédération peuvent accéder à cette méthode.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "403", description = "Accès refusé")
+ })
public Uni> getCurrentSaisonAffiliationAdmin() {
return service.getCurrentSaisonAffiliation();
}
@@ -125,15 +58,30 @@ public class AffiliationEndpoints {
@Path("{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
- public Uni> getAffiliation(@PathParam("id") long id) {
+ @Operation(summary = "Renvoie les affiliations pour un club", description = "Cette méthode renvoie les affiliations pour un club donné. Seuls les administrateurs de la fédération et les présidents, secrétaires et responsables intranet du club peuvent accéder à cette méthode.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Club non trouvé")
+ })
+ public Uni> getAffiliation(
+ @Parameter(description = "L'identifiant du club") @PathParam("id") long id) {
return Uni.createFrom().item(id).invoke(checkPerm).chain(__ -> service.getAffiliation(id));
}
- @PUT
+ @POST
@Path("{id}")
@RolesAllowed("federation_admin")
@Produces(MediaType.APPLICATION_JSON)
- public Uni setAffiliation(@PathParam("id") long id, @QueryParam("saison") int saison) {
+ @Operation(summary = "Ajoute une affiliation pour un club", description = "Cette méthode ajoute une affiliation pour un club et une saison donné. Seuls les administrateurs de la fédération peuvent accéder à cette méthode.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Club non trouvé")
+ })
+ public Uni setAffiliation(
+ @Parameter(description = "L'identifiant du club") @PathParam("id") long id,
+ @Parameter(description = "La saison à pour la quelle ajoute l'affiliation") @QueryParam("saison") int saison) {
return service.setAffiliation(id, saison);
}
@@ -141,22 +89,13 @@ public class AffiliationEndpoints {
@Path("{id}")
@RolesAllowed("federation_admin")
@Produces(MediaType.TEXT_PLAIN)
- public Uni> deleteAffiliation(@PathParam("id") long id) {
+ @Operation(summary = "Supprime une affiliation", description = "Cette méthode supprime l'affiliation {id}. Seuls les administrateurs de la fédération peuvent accéder à cette méthode.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "204", description = "Réussite"),
+ @APIResponse(responseCode = "403", description = "Accès refusé")
+ })
+ public Uni> deleteAffiliation(
+ @Parameter(description = "L'identifiant de l'affiliation") @PathParam("id") long id) {
return service.deleteAffiliation(id);
}
-
- @GET
- @Path("/request/{id}/logo")
- @RolesAllowed({"federation_admin"})
- public Uni getLogo(@PathParam("id") long id) throws URISyntaxException {
- return Utils.getMediaFile(id, media, "aff_request/logo", Uni.createFrom().nullItem());
- }
-
- @GET
- @Path("/request/{id}/status")
- @RolesAllowed({"federation_admin"})
- public Uni getStatus(@PathParam("id") long id) throws URISyntaxException {
- return Utils.getMediaFile(id, media, "aff_request/status", "affiliation_request_" + id + ".pdf",
- Uni.createFrom().nullItem());
- }
}
diff --git a/src/main/java/fr/titionfire/ffsaf/rest/AffiliationRequestEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationRequestEndpoints.java
new file mode 100644
index 0000000..b8a8bb5
--- /dev/null
+++ b/src/main/java/fr/titionfire/ffsaf/rest/AffiliationRequestEndpoints.java
@@ -0,0 +1,198 @@
+package fr.titionfire.ffsaf.rest;
+
+import fr.titionfire.ffsaf.domain.service.AffiliationService;
+import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation;
+import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliationResume;
+import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
+import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
+import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm;
+import fr.titionfire.ffsaf.utils.GroupeUtils;
+import fr.titionfire.ffsaf.utils.Utils;
+import io.quarkus.oidc.IdToken;
+import io.quarkus.security.identity.SecurityIdentity;
+import io.smallrye.mutiny.Uni;
+import io.smallrye.mutiny.unchecked.Unchecked;
+import jakarta.annotation.security.RolesAllowed;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
+
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Path("api/affiliation/request")
+public class AffiliationRequestEndpoints {
+
+ @Inject
+ AffiliationService service;
+
+ @Inject
+ @IdToken
+ JsonWebToken idToken;
+
+ @Inject
+ SecurityIdentity securityIdentity;
+
+ @ConfigProperty(name = "upload_dir")
+ String media;
+
+ Consumer checkPerm = Unchecked.consumer(id -> {
+ if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken))
+ throw new DForbiddenException();
+ });
+
+ @GET
+ @Path("")
+ @RolesAllowed({"federation_admin"})
+ @Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Renvoie toutes les demandes d'affiliation", description = "Cette méthode renvoie toutes les " +
+ "demandes d'affiliation sous forme de résumés.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "403", description = "Accès refusé")
+ })
+ public Uni> getAllAffRequest() {
+ return service.getAllReq().map(o -> o.stream().map(SimpleReqAffiliationResume::fromModel).toList());
+ }
+
+ @POST
+ @Path("")
+ @Produces(MediaType.TEXT_PLAIN)
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Operation(summary = "Enregistre une nouvelle demande d'affiliation", description = "Cette méthode enregistre une " +
+ "nouvelle demande d'affiliation à partir des données soumises dans le formulaire. Ne nécessite pas d'authentification.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "400", description = "Données invalides"),
+ @APIResponse(responseCode = "403", description = "Accès refusé")
+ })
+ public Uni saveAffRequest(AffiliationRequestForm form) {
+ return service.save(form);
+ }
+
+ @GET
+ @Path("/{id}")
+ @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
+ @Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Renvoie une demande d'affiliation", description = "Cette méthode renvoie une demande d'affiliation " +
+ "pour l'identifiant spécifié.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Demande d'affiliation non trouvée")
+ })
+ public Uni getAffRequest(
+ @Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id) {
+ return service.getRequest(id).invoke(Unchecked.consumer(o -> {
+ if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin"))
+ throw new DForbiddenException();
+ })).invoke(o -> checkPerm.accept(o.getClub()));
+ }
+
+ @DELETE
+ @Path("/{id}")
+ @RolesAllowed({"federation_admin"})
+ @Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Supprime une demande d'affiliation", description = "Cette méthode supprime une demande " +
+ "d'affiliation pour l'identifiant spécifié.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "204", description = "Réussite"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Demande d'affiliation non trouvée")
+ })
+ public Uni> getDelAffRequest(
+ @Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id) {
+ return service.getRequest(id).invoke(Unchecked.consumer(o -> {
+ if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin"))
+ throw new DForbiddenException();
+ })).invoke(o -> checkPerm.accept(o.getClub()))
+ .chain(o -> service.deleteReqAffiliation(id));
+ }
+
+ @PUT
+ @Path("/save")
+ @RolesAllowed({"federation_admin"})
+ @Produces(MediaType.TEXT_PLAIN)
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Operation(summary = "Enregistre une demande d'affiliation en tant qu'admin", description = "Cette méthode " +
+ "enregistre une demande d'affiliation en tant qu'admin à partir des données soumises dans le formulaire.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "400", description = "Données invalides"),
+ @APIResponse(responseCode = "403", description = "Accès refusé")
+ })
+ public Uni> saveAdminAffRequest(AffiliationRequestSaveForm form) {
+ return service.saveAdmin(form);
+ }
+
+ @PUT
+ @Path("/edit")
+ @RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
+ @Produces(MediaType.TEXT_PLAIN)
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Operation(summary = "Modifie une demande d'affiliation", description = "Cette méthode modifie une demande " +
+ "d'affiliation à partir des données soumises dans le formulaire.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "400", description = "Données invalides"),
+ @APIResponse(responseCode = "403", description = "Accès refusé")
+ })
+ public Uni> saveEditAffRequest(AffiliationRequestForm form) {
+ return service.saveEdit(form);
+ }
+
+ @PUT
+ @Path("/apply")
+ @RolesAllowed({"federation_admin"})
+ @Produces(MediaType.TEXT_PLAIN)
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Operation(summary = "Accepte une demande d'affiliation", description = "Cette méthode accepte une demande " +
+ "d'affiliation à partir des données soumises dans le formulaire.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "400", description = "Données invalides"),
+ @APIResponse(responseCode = "403", description = "Accès refusé")
+ })
+ public Uni> acceptAffRequest(AffiliationRequestSaveForm form) {
+ return service.accept(form);
+ }
+
+ @GET
+ @Path("/{id}/logo")
+ @RolesAllowed({"federation_admin"})
+ @Operation(summary = "Renvoie le logo d'une demande d'affiliation", description = "Cette méthode renvoie le logo" +
+ " d'une demande d'affiliation pour l'identifiant spécifié.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Logo non trouvé")
+ })
+ public Uni getLogo(
+ @Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id) throws URISyntaxException {
+ return Utils.getMediaFile(id, media, "aff_request/logo", Uni.createFrom().nullItem());
+ }
+
+ @GET
+ @Path("/{id}/status")
+ @RolesAllowed({"federation_admin"})
+ @Operation(summary = "Renvoie le statut d'une demande d'affiliation", description = "Cette méthode renvoie le statut" +
+ " d'une demande d'affiliation pour l'identifiant spécifié.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Statut non trouvé")
+ })
+ public Uni getStatus(
+ @Parameter(description = "L'identifiant de la demande d'affiliation") @PathParam("id") long id) throws URISyntaxException {
+ return Utils.getMediaFile(id, media, "aff_request/status", "affiliation_request_" + id + ".pdf",
+ Uni.createFrom().nullItem());
+ }
+}
diff --git a/src/main/java/fr/titionfire/ffsaf/rest/AssoEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/AssoEndpoints.java
index d739ca7..7fec841 100644
--- a/src/main/java/fr/titionfire/ffsaf/rest/AssoEndpoints.java
+++ b/src/main/java/fr/titionfire/ffsaf/rest/AssoEndpoints.java
@@ -6,8 +6,14 @@ import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import io.smallrye.mutiny.Uni;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
+import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.eclipse.microprofile.rest.client.inject.RestClient;
+@Tag(name = "Association", description = "Récupération des informations d'une association depuis la base de données Française")
@Path("api/asso")
public class AssoEndpoints {
@@ -17,7 +23,14 @@ public class AssoEndpoints {
@GET
@Path("siren/{siren}")
@Produces(MediaType.APPLICATION_JSON)
- public Uni getInfoSiren(@PathParam("siren") String siren) {
+ @Operation(summary = "Renvoie les informations d'une association à partir de son numéro SIREN",
+ description = "Cette méthode renvoie les informations d'une association à partir de son numéro SIREN.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "404", description = "Numéro SIREN introuvable")
+ })
+ public Uni getInfoSiren(
+ @Parameter(description = "Le numéro SIREN de l'association à récupérer") @PathParam("siren") String siren) {
return sirenService.get_unite(siren).onFailure().transform(throwable -> {
if (throwable instanceof WebApplicationException exception) {
if (exception.getResponse().getStatus() == 400)
diff --git a/src/main/java/fr/titionfire/ffsaf/rest/AuthEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/AuthEndpoints.java
index 3371743..ee26e80 100644
--- a/src/main/java/fr/titionfire/ffsaf/rest/AuthEndpoints.java
+++ b/src/main/java/fr/titionfire/ffsaf/rest/AuthEndpoints.java
@@ -11,6 +11,9 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import java.net.URI;
import java.net.URISyntaxException;
@@ -29,6 +32,12 @@ public class AuthEndpoints {
@GET
@Produces(MediaType.TEXT_PLAIN)
+ @Operation(summary = "Vérifie si l'utilisateur est authentifié", description = "Cette méthode renvoie true si " +
+ "l'utilisateur est authentifié et false sinon.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite")
+ })
+
public Boolean auth() {
return !securityIdentity.isAnonymous();
}
@@ -37,6 +46,12 @@ public class AuthEndpoints {
@Path("/userinfo")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Renvoie les informations de l'utilisateur authentifié", description = "Cette méthode renvoie les" +
+ " informations de l'utilisateur authentifié sous forme d'objet JSON.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Réussite"),
+ @APIResponse(responseCode = "401", description = "Utilisateur non authentifié")
+ })
public UserInfo userinfo() {
return UserInfo.makeUserInfo(accessToken, securityIdentity);
}
@@ -45,6 +60,11 @@ public class AuthEndpoints {
@Path("/login")
@Authenticated
@Produces(MediaType.TEXT_PLAIN)
+ @Operation(summary = "Redirige l'utilisateur vers la page de connexion", description = "Cette méthode redirige " +
+ "l'utilisateur vers la page de connexion.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "307", description = "Redirection temporaire")
+ })
public Response login() throws URISyntaxException {
return Response.temporaryRedirect(new URI(redirect)).build();
}
diff --git a/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java
index ef778ed..cdfa9f8 100644
--- a/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java
+++ b/src/main/java/fr/titionfire/ffsaf/rest/ClubEndpoints.java
@@ -27,12 +27,18 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.eclipse.microprofile.openapi.annotations.Operation;
+import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
+import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
+@Tag(name = "Club", description = "Gestion des clubs")
@Path("api/club")
public class ClubEndpoints {
@@ -63,6 +69,12 @@ public class ClubEndpoints {
@Path("/no_detail")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Renvoie la liste de tous les clubs sans détails", description = "Renvoie la liste de tous les " +
+ "clubs sans les détails des membres et des affiliations")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "La liste de tous les clubs sans détails"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
public Uni> getAll() {
return clubService.getAll().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList());
}
@@ -71,6 +83,8 @@ public class ClubEndpoints {
@Path("/contact_type")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Renvoie les types de contacts pour les clubs", description = "Renvoie la liste des types de " +
+ "contacts possibles pour les clubs")
public Uni> getConcatType() {
return Uni.createFrom().item(Contact.toSite());
}
@@ -79,10 +93,17 @@ public class ClubEndpoints {
@Path("/find")
@RolesAllowed({"federation_admin"})
@Produces(MediaType.APPLICATION_JSON)
- public Uni> getFindAdmin(@QueryParam("limit") Integer limit,
- @QueryParam("page") Integer page,
- @QueryParam("search") String search,
- @QueryParam("country") String country) {
+ @Operation(summary = "Recherche des clubs en fonction de critères de recherche", description = "Recherche des clubs " +
+ "en fonction de critères de recherche tels que le nom, le pays, etc.")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "La liste des clubs correspondant aux critères de recherche"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
+ public Uni> getFindAdmin(
+ @Parameter(description = "Nombre max de résulta (max 50)") @QueryParam("limit") Integer limit,
+ @Parameter(description = "Page à consulter") @QueryParam("page") Integer page,
+ @Parameter(description = "Text à rechercher") @QueryParam("search") String search,
+ @Parameter(description = "Pays à filter") @QueryParam("country") String country) {
if (limit == null)
limit = 50;
if (page == null || page < 1)
@@ -95,7 +116,16 @@ public class ClubEndpoints {
@Path("{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
- public Uni getById(@PathParam("id") long id) {
+ @Operation(summary = "Renvoie les détails d'un club en fonction de son identifiant", description = "Renvoie les " +
+ "détails d'un club en fonction de son identifiant, y compris les informations sur les membres et les affiliations")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Les détails du club"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Le club n'existe pas"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
+ public Uni getById(
+ @Parameter(description = "Identifiant de club") @PathParam("id") long id) {
return clubService.getById(id).onItem().invoke(checkPerm).map(SimpleClub::fromModel).invoke(m -> {
m.setContactMap(Contact.toSite());
});
@@ -106,7 +136,17 @@ public class ClubEndpoints {
@RolesAllowed({"federation_admin"})
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
- public Uni setAdminClub(@PathParam("id") long id, FullClubForm input) {
+ @Operation(summary = "Met à jour les informations d'un club en fonction de son identifiant", description = "Met à " +
+ "jour les informations d'un club en fonction de son identifiant, y compris les informations sur les membres" +
+ " et les affiliations")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Le club a été mis à jour avec succès"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Le club n'existe pas"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
+ public Uni setAdminClub(
+ @Parameter(description = "Identifiant de club") @PathParam("id") long id, FullClubForm input) {
return clubService.update(id, input)
.invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
@@ -135,6 +175,13 @@ public class ClubEndpoints {
@RolesAllowed({"federation_admin"})
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Operation(summary = "Ajoute un nouveau club", description = "Ajoute un nouveau club avec les informations fournies" +
+ " dans le formulaire")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Le club a été ajouté avec succès"),
+ @APIResponse(responseCode = "400", description = "Les données envoyées sont invalides"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
public Uni addAdminClub(FullClubForm input) {
return clubService.add(input)
.invoke(Unchecked.consumer(id -> {
@@ -158,7 +205,16 @@ public class ClubEndpoints {
@Path("{id}")
@RolesAllowed({"federation_admin"})
@Produces(MediaType.TEXT_PLAIN)
- public Uni> deleteAdminClub(@PathParam("id") long id) {
+ @Operation(summary = "Supprime un club en fonction de son identifiant", description = "Supprime un club en fonction" +
+ " de son identifiant, ainsi que toutes les informations associées")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "204", description = "Le club a été supprimé avec succès"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Le club n'existe pas"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
+ public Uni> deleteAdminClub(
+ @Parameter(description = "Identifiant de club") @PathParam("id") long id) {
return clubService.delete(id);
}
@@ -166,6 +222,14 @@ public class ClubEndpoints {
@Path("/me")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Renvoie les informations du club de l'utilisateur connecté", description = "Renvoie les " +
+ "informations du club de l'utilisateur connecté")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Les informations du club de l'utilisateur connecté"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "L'utilisateur n'est pas membre d'un club"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
public Uni getOfUser() {
return clubService.getOfUser(idToken).map(SimpleClub::fromModel)
.invoke(m -> m.setContactMap(Contact.toSite()));
@@ -176,6 +240,14 @@ public class ClubEndpoints {
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Operation(summary = "Met à jour les informations du club de l'utilisateur connecté", description = "Met à jour les" +
+ " informations du club de l'utilisateur connecté")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Les informations du club de l'utilisateur connecté ont été mises à jour avec succès"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "L'utilisateur n'est pas membre d'un club"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
public Uni setClubOfUser(PartClubForm form) {
return clubService.updateOfUser(idToken, form);
}
@@ -184,6 +256,7 @@ public class ClubEndpoints {
@Path("/renew/{id}")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
+ @Operation(hidden = true)
public Uni getOfUser(@PathParam("id") long id, @QueryParam("m1") long m1_id,
@QueryParam("m2") long m2_id, @QueryParam("m3") long m3_id) {
return Uni.createFrom().item(id).invoke(checkPerm2)
@@ -194,14 +267,31 @@ public class ClubEndpoints {
@Path("/desk/{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
- public Uni> getClubDesk(@PathParam("id") long id) {
+ @Operation(summary = "Renvoie la liste des membres du bureau du club", description = "Renvoie la liste des membres " +
+ "du bureau du club spécifié")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "La liste des membres du bureau du club"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Le club n'existe pas"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
+ public Uni> getClubDesk(
+ @Parameter(description = "Identifiant de club") @PathParam("id") long id) {
return clubService.getClubDesk(checkPerm, id);
}
@GET
@Path("{clubId}/logo")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
- public Uni getLogo(@PathParam("clubId") String clubId) {
+ @Operation(summary = "Renvoie le logo du club", description = "Renvoie le logo du club spécifié")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Le logo du club"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Le club n'existe pas"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
+ public Uni getLogo(
+ @Parameter(description = "Identifiant long (clubId) de club") @PathParam("clubId") String clubId) {
return clubService.getByClubId(clubId).onItem().invoke(checkPerm).chain(Unchecked.function(clubModel -> {
try {
return Utils.getMediaFile((clubModel != null) ? clubModel.getId() : -1, media, "ppClub",
@@ -215,7 +305,15 @@ public class ClubEndpoints {
@GET
@Path("{id}/status")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire"})
- public Uni getStatus(@PathParam("id") long id) {
+ @Operation(summary = "Renvoie le statut du club", description = "Renvoie le statut du club spécifié")
+ @APIResponses(value = {
+ @APIResponse(responseCode = "200", description = "Le statut du club"),
+ @APIResponse(responseCode = "403", description = "Accès refusé"),
+ @APIResponse(responseCode = "404", description = "Le club n'existe pas"),
+ @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
+ })
+ public Uni getStatus(
+ @Parameter(description = "Identifiant de club") @PathParam("id") long id) {
return clubService.getById(id).onItem().invoke(checkPerm).chain(Unchecked.function(clubModel -> {
try {
return Utils.getMediaFile(clubModel.getId(), media, "clubStatus",
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 92aa416..2ceeada 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -17,7 +17,6 @@ quarkus.quartz.start-mode=forced
%dev.quarkus.log.min-level=ALL
%dev.quarkus.log.category."fr.titionfire.ffsaf".level=ALL
-
quarkus.oidc.auth-server-url=https://auth.safca.fr/realms/safca
quarkus.oidc.client-id=backend
quarkus.oidc.credentials.secret=secret
diff --git a/src/main/webapp/src/pages/admin/club/AffiliationCard.jsx b/src/main/webapp/src/pages/admin/club/AffiliationCard.jsx
index 96290e7..0d7e7f7 100644
--- a/src/main/webapp/src/pages/admin/club/AffiliationCard.jsx
+++ b/src/main/webapp/src/pages/admin/club/AffiliationCard.jsx
@@ -67,7 +67,7 @@ function sendAffiliation(event, dispatch) {
const formData = new FormData(event.target);
toast.promise(
- apiAxios.put(`/affiliation/${formData.get('club')}?saison=${formData.get('saison')}`),
+ apiAxios.post(`/affiliation/${formData.get('club')}?saison=${formData.get('saison')}`),
{
pending: "Enregistrement de l'affiliation en cours",
success: "Affiliation enregistrée avec succès 🎉",
diff --git a/src/main/webapp/vite.config.js b/src/main/webapp/vite.config.js
index a9c4187..c532ed0 100644
--- a/src/main/webapp/vite.config.js
+++ b/src/main/webapp/vite.config.js
@@ -17,6 +17,10 @@ export default ({mode}) => {
target: process.env.VITE_API_URL,
changeOrigin: true,
},
+ "/q": {
+ target: process.env.VITE_API_URL,
+ changeOrigin: true,
+ },
},
},
});