Merge pull request 'dev' (#1) from dev into master
Reviewed-on: https://git.poupouche.fr/Thibaut/ffsaf-site/pulls/1
This commit is contained in:
commit
3c8721d706
41
.gitea/workflows/deploy_in_prod.yml
Normal file
41
.gitea/workflows/deploy_in_prod.yml
Normal file
@ -0,0 +1,41 @@
|
||||
name: Deploy Production Server
|
||||
|
||||
# Only run the workflow when a PR is merged on main and closed
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
branches:
|
||||
- 'master'
|
||||
|
||||
# Here we check that the PR was correctly merged to main
|
||||
jobs:
|
||||
if_merged:
|
||||
if: github.event.pull_request.merged == true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'graalvm'
|
||||
cache: 'maven'
|
||||
|
||||
- name: Build site
|
||||
run: |
|
||||
cp vite.env src/main/webapp/.env
|
||||
cd src/main/webapp
|
||||
npm install
|
||||
npm run build
|
||||
cd ../../..
|
||||
rm -rf src/main/resources/META-INF/resources
|
||||
mkdir -p src/main/resources/META-INF/
|
||||
mv dist src/main/resources/META-INF/resources
|
||||
|
||||
- name: Build application
|
||||
run: |
|
||||
cp ../vite.env src/main/webapp/.env
|
||||
chmod 740 mvnw
|
||||
./mvnw package -Pnative -DskipTests
|
||||
26
pom.xml
26
pom.xml
@ -109,6 +109,32 @@
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-websockets</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sf.jmimemagic</groupId>
|
||||
<artifactId>jmimemagic</artifactId>
|
||||
<version>0.1.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-smallrye-openapi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-swagger-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-cache</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.librepdf</groupId>
|
||||
<artifactId>openpdf</artifactId>
|
||||
<version>2.0.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@ -2,6 +2,7 @@ package fr.titionfire;
|
||||
|
||||
import io.quarkus.oidc.IdToken;
|
||||
import io.quarkus.oidc.RefreshToken;
|
||||
import io.quarkus.oidc.UserInfo;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.GET;
|
||||
@ -30,6 +31,9 @@ public class ExampleResource {
|
||||
@IdToken
|
||||
JsonWebToken idToken;
|
||||
|
||||
@Inject
|
||||
UserInfo userInfo;
|
||||
|
||||
/**
|
||||
* Injection point for the Access Token issued by the OpenID Connect Provider
|
||||
*/
|
||||
@ -59,7 +63,8 @@ public class ExampleResource {
|
||||
.append("<body>")
|
||||
.append("<ul>");
|
||||
|
||||
|
||||
System.out.println(idToken);
|
||||
System.out.println(accessToken);
|
||||
Object userName = this.idToken.getClaim("preferred_username");
|
||||
|
||||
if (userName != null) {
|
||||
@ -69,25 +74,17 @@ public class ExampleResource {
|
||||
response.append("<li>username: ").append(this.idToken.toString()).append("</li>");
|
||||
}
|
||||
|
||||
Object scopes = this.accessToken.getClaim("scope");
|
||||
/*Object scopes = this.accessToken.getClaim("scope");
|
||||
|
||||
if (scopes != null) {
|
||||
response.append("<li>scopes: ").append(scopes.toString()).append("</li>");
|
||||
}
|
||||
|
||||
if (scopes != null) {
|
||||
response.append("<li>scopes: ").append(this.accessToken.toString()).append("</li>");
|
||||
}
|
||||
response.append("<li>scopes: ").append(this.accessToken.toString()).append("</li>");
|
||||
response.append("<li>scopes: ").append(this.accessToken.getClaim("user_groups").toString()).append("</li>");*/
|
||||
|
||||
|
||||
if (scopes != null) {
|
||||
response.append("<li>scopes: ").append(this.accessToken.getClaim("user_groups").toString()).append("</li>");
|
||||
}
|
||||
|
||||
if (scopes != null) {
|
||||
response.append("<li>getRoles: ").append(this.securityIdentity.getRoles()).append("</li>");
|
||||
}
|
||||
|
||||
response.append("<li>getRoles: ").append(this.securityIdentity.getRoles()).append("</li>");
|
||||
response.append("<li>refresh_token: ").append(refreshToken.getToken() != null).append("</li>");
|
||||
|
||||
return response.append("</ul>").append("</body>").append("</html>").toString();
|
||||
|
||||
27
src/main/java/fr/titionfire/PingPage.java
Normal file
27
src/main/java/fr/titionfire/PingPage.java
Normal file
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,17 +1,15 @@
|
||||
package fr.titionfire;
|
||||
|
||||
import io.quarkus.qute.Template;
|
||||
import io.quarkus.qute.TemplateInstance;
|
||||
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@Path("/some-page")
|
||||
@Path("api/some-page")
|
||||
public class SomePage {
|
||||
|
||||
private final Template page;
|
||||
@ -22,8 +20,11 @@ public class SomePage {
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
public TemplateInstance get(@QueryParam("name") String name) {
|
||||
return page.data("name", name);
|
||||
public Uni<String> get() {
|
||||
return Uni.createFrom()
|
||||
.completionStage(() -> page
|
||||
.data("name", "test")
|
||||
.renderAsync());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package fr.titionfire.ffsaf.data.model;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ -16,11 +17,13 @@ import lombok.*;
|
||||
public class AffiliationModel {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Schema(description = "Identifiant de l'affiliation", example = "42")
|
||||
Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "club", referencedColumnName = "id")
|
||||
ClubModel club;
|
||||
|
||||
@Schema(description = "Saison de l'affiliation", example = "2021")
|
||||
int saison;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package fr.titionfire.ffsaf.data.model;
|
||||
|
||||
import fr.titionfire.ffsaf.utils.RoleAsso;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
@ -20,24 +21,27 @@ public class AffiliationRequestModel {
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
String siren;
|
||||
long siret;
|
||||
String RNA;
|
||||
String address;
|
||||
|
||||
String president_lname;
|
||||
String president_fname;
|
||||
String president_email;
|
||||
int president_lincence;
|
||||
String m1_lname;
|
||||
String m1_fname;
|
||||
String m1_email;
|
||||
int m1_lincence;
|
||||
RoleAsso m1_role;
|
||||
|
||||
String tresorier_lname;
|
||||
String tresorier_fname;
|
||||
String tresorier_email;
|
||||
int tresorier_lincence;
|
||||
String m2_lname;
|
||||
String m2_fname;
|
||||
String m2_email;
|
||||
int m2_lincence;
|
||||
RoleAsso m2_role;
|
||||
|
||||
String secretaire_lname;
|
||||
String secretaire_fname;
|
||||
String secretaire_email;
|
||||
int secretaire_lincence;
|
||||
String m3_lname;
|
||||
String m3_fname;
|
||||
String m3_email;
|
||||
int m3_lincence;
|
||||
RoleAsso m3_role;
|
||||
|
||||
int saison;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import fr.titionfire.ffsaf.utils.Contact;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -20,37 +21,55 @@ import java.util.Map;
|
||||
public class ClubModel {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Schema(description = "Identifiant du club", example = "1")
|
||||
Long id;
|
||||
|
||||
@Schema(description = "Identifiant long du club (UUID)", example = "b94f3167-3f6a-449c-a73b-ec84202bf07e")
|
||||
String clubId;
|
||||
|
||||
@Schema(description = "Nom du club", example = "Association sportive")
|
||||
String name;
|
||||
|
||||
@Schema(description = "Pays du club", example = "FR")
|
||||
String country;
|
||||
|
||||
String shieldURL;
|
||||
|
||||
//@Enumerated(EnumType.STRING)
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "club_contact_mapping",
|
||||
joinColumns = {@JoinColumn(name = "club_id", referencedColumnName = "id")})
|
||||
@MapKeyColumn(name = "contact_type")
|
||||
@Schema(description = "Les contacts du club", example = "{\"SITE\": \"www.test.com\", \"COURRIEL\": \"test@test.com\"}")
|
||||
Map<Contact, String> contact;
|
||||
|
||||
@Lob
|
||||
@Column(length = 4096)
|
||||
@Schema(description = "Liste des lieux d'entraînement", example = "[{\"text\":\"addr 1\",\"lng\":2.24654,\"lat\":52.4868658},{\"text\":\"addr 2\",\"lng\":2.88654,\"lat\":52.7865456}]")
|
||||
String training_location;
|
||||
|
||||
@Lob
|
||||
@Column(length = 4096)
|
||||
@Schema(description = "Liste des jours et horaires d'entraînement (jours 0-6, 0=>lundi) (temps en minute depuis 00:00, 122=>2h02)", example = "[{\"day\":0,\"time_start\":164,\"time_end\":240},{\"day\":3,\"time_start\":124,\"time_end\":250}]")
|
||||
String training_day_time;
|
||||
|
||||
@Schema(description = "Contact interne du club", example = "john.doe@test.com")
|
||||
String contact_intern;
|
||||
|
||||
@Schema(description = "Adresse postale du club", example = "1 rue de l'exemple, 75000 Paris")
|
||||
String address;
|
||||
|
||||
@Schema(description = "RNA du club", example = "W123456789")
|
||||
String RNA;
|
||||
|
||||
String SIRET;
|
||||
@Schema(description = "Numéro SIRET du club", example = "12345678901234")
|
||||
Long SIRET;
|
||||
|
||||
String no_affiliation;
|
||||
@Schema(description = "Numéro d'affiliation du club", example = "12345")
|
||||
Long no_affiliation;
|
||||
|
||||
@Schema(description = "Club international", example = "false")
|
||||
boolean international;
|
||||
|
||||
@OneToMany(mappedBy = "club", fetch = FetchType.EAGER)
|
||||
@OneToMany(mappedBy = "club", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||
@Schema(description = "Liste des affiliations du club (optionnel)")
|
||||
List<AffiliationModel> affiliations;
|
||||
}
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
package fr.titionfire.ffsaf.data.model;
|
||||
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
|
||||
@Entity
|
||||
@Table(name = "compet")
|
||||
public class CompetitionModel {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
Long id;
|
||||
|
||||
@Column(name = "system_type")
|
||||
CompetitionSystem system;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "club", referencedColumnName = "id")
|
||||
ClubModel club;
|
||||
|
||||
String name;
|
||||
|
||||
String uuid;
|
||||
|
||||
Date date;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(name = "register",
|
||||
uniqueConstraints = @UniqueConstraint(columnNames = {"id_competition", "id_membre"}),
|
||||
joinColumns = @JoinColumn(name = "id_competition"),
|
||||
inverseJoinColumns = @JoinColumn(name = "id_membre"))
|
||||
List<MembreModel> insc;
|
||||
|
||||
String owner;
|
||||
}
|
||||
@ -3,6 +3,7 @@ package fr.titionfire.ffsaf.data.model;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ -16,15 +17,20 @@ import lombok.*;
|
||||
public class LicenceModel {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Schema(description = "L'identifiant de la licence.")
|
||||
Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "membre", referencedColumnName = "id")
|
||||
@Schema(description = "Le membre de la licence. (optionnel)")
|
||||
MembreModel membre;
|
||||
|
||||
@Schema(description = "La saison de la licence.", example = "2025")
|
||||
int saison;
|
||||
|
||||
boolean certificate;
|
||||
@Schema(description = "Nom du médecin sur certificat médical.", example = "M. Jean")
|
||||
String certificate;
|
||||
|
||||
@Schema(description = "Licence validée", example = "true")
|
||||
boolean validate;
|
||||
}
|
||||
|
||||
56
src/main/java/fr/titionfire/ffsaf/data/model/MatchModel.java
Normal file
56
src/main/java/fr/titionfire/ffsaf/data/model/MatchModel.java
Normal file
@ -0,0 +1,56 @@
|
||||
package fr.titionfire.ffsaf.data.model;
|
||||
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
|
||||
@Entity
|
||||
@ToString
|
||||
@Table(name = "match")
|
||||
public class MatchModel {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
Long id;
|
||||
|
||||
@Column(name = "system_type")
|
||||
CompetitionSystem system;
|
||||
Long systemId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "c1", referencedColumnName = "id")
|
||||
MembreModel c1_id = null;
|
||||
|
||||
String c1_str = null;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "c2", referencedColumnName = "id")
|
||||
MembreModel c2_id = null;
|
||||
|
||||
String c2_str = null;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "id_poule", referencedColumnName = "id")
|
||||
PouleModel poule = null;
|
||||
|
||||
long poule_ord = 0;
|
||||
|
||||
boolean isEnd = true;
|
||||
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
@CollectionTable(name = "score", joinColumns = @JoinColumn(name = "id_match"))
|
||||
List<ScoreEmbeddable> scores = new ArrayList<>();
|
||||
|
||||
char groupe = 'A';
|
||||
}
|
||||
@ -7,6 +7,7 @@ import fr.titionfire.ffsaf.utils.RoleAsso;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@ -24,35 +25,50 @@ public class MembreModel {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Schema(description = "L'identifiant du membre.", example = "1")
|
||||
Long id;
|
||||
|
||||
@Schema(description = "L'identifiant long du membre (userID).", example = "e81d1d35-d897-421e-8086-6c5e74d13c6e")
|
||||
String userId;
|
||||
|
||||
@Schema(description = "Le nom du membre.", example = "Dupont")
|
||||
String lname;
|
||||
@Schema(description = "Le prénom du membre.", example = "Jean")
|
||||
String fname;
|
||||
|
||||
@Schema(description = "La catégorie du membre.", example = "SENIOR")
|
||||
Categorie categorie;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "club", referencedColumnName = "id")
|
||||
@Schema(description = "Le club du membre.")
|
||||
ClubModel club;
|
||||
|
||||
@Schema(description = "Le genre du membre.", example = "H")
|
||||
Genre genre;
|
||||
|
||||
@Schema(description = "Le numéro de licence du membre.", example = "12345")
|
||||
int licence;
|
||||
|
||||
@Schema(description = "Le pays du membre.", example = "FR")
|
||||
String country;
|
||||
|
||||
@Schema(description = "La date de naissance du membre.")
|
||||
Date birth_date;
|
||||
|
||||
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com")
|
||||
String email;
|
||||
|
||||
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE")
|
||||
RoleAsso role;
|
||||
|
||||
@Schema(description = "Le grade d'arbitrage du membre.", example = "NA")
|
||||
GradeArbitrage grade_arbitrage;
|
||||
|
||||
@Schema(hidden = true)
|
||||
String url_photo;
|
||||
|
||||
@OneToMany(mappedBy = "membre", fetch = FetchType.LAZY)
|
||||
@OneToMany(mappedBy = "membre", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||
@Schema(description = "Les licences du membre. (optionnel)")
|
||||
List<LicenceModel> licences;
|
||||
}
|
||||
|
||||
45
src/main/java/fr/titionfire/ffsaf/data/model/PouleModel.java
Normal file
45
src/main/java/fr/titionfire/ffsaf/data/model/PouleModel.java
Normal file
@ -0,0 +1,45 @@
|
||||
package fr.titionfire.ffsaf.data.model;
|
||||
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
|
||||
@Entity
|
||||
@Table(name = "poule")
|
||||
public class PouleModel {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
Long id;
|
||||
|
||||
@Column(name = "system_type")
|
||||
CompetitionSystem system;
|
||||
Long systemId;
|
||||
|
||||
String name = "";
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "id_compet", referencedColumnName = "id")
|
||||
CompetitionModel compet;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "id_poule", referencedColumnName = "id")
|
||||
List<MatchModel> matchs;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "id_poule", referencedColumnName = "id")
|
||||
List<TreeModel> tree;
|
||||
|
||||
Integer type;
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package fr.titionfire.ffsaf.data.model;
|
||||
|
||||
import fr.titionfire.ffsaf.utils.SequenceType;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.*;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
|
||||
@Entity
|
||||
@Table(name = "sequence")
|
||||
public class SequenceModel {
|
||||
@Id
|
||||
SequenceType type;
|
||||
|
||||
long value;
|
||||
}
|
||||
39
src/main/java/fr/titionfire/ffsaf/data/model/TreeModel.java
Normal file
39
src/main/java/fr/titionfire/ffsaf/data/model/TreeModel.java
Normal file
@ -0,0 +1,39 @@
|
||||
package fr.titionfire.ffsaf.data.model;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
|
||||
@Entity
|
||||
@Table(name = "tree")
|
||||
public class TreeModel {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
Long id;
|
||||
|
||||
@Column(name = "id_poule")
|
||||
Long poule;
|
||||
|
||||
Integer level;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "match_id", referencedColumnName = "id")
|
||||
MatchModel match;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
|
||||
@JoinColumn(referencedColumnName = "id")
|
||||
TreeModel left;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
|
||||
@JoinColumn(referencedColumnName = "id")
|
||||
TreeModel right;
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package fr.titionfire.ffsaf.data.repository;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.AffiliationModel;
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class AffiliationRepository implements PanacheRepositoryBase<AffiliationModel, Long> {
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package fr.titionfire.ffsaf.data.repository;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.CompetitionModel;
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class CompetitionRepository implements PanacheRepositoryBase<CompetitionModel, Long> {
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package fr.titionfire.ffsaf.data.repository;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class MatchRepository implements PanacheRepositoryBase<MatchModel, Long> {
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package fr.titionfire.ffsaf.data.repository;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.PouleModel;
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class PouleRepository implements PanacheRepositoryBase<PouleModel, Long> {
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package fr.titionfire.ffsaf.data.repository;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.SequenceModel;
|
||||
import fr.titionfire.ffsaf.utils.SequenceType;
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class SequenceRepository implements PanacheRepositoryBase<SequenceModel, SequenceType> {
|
||||
|
||||
public Uni<Long> getNextValueInTransaction(SequenceType type) {
|
||||
return this.findById(type).onItem().ifNull()
|
||||
.switchTo(() -> this.persist(new SequenceModel(type, 1L)))
|
||||
.chain(v -> {
|
||||
v.setValue(v.getValue() + 1);
|
||||
return this.persistAndFlush(v);
|
||||
})
|
||||
.map(SequenceModel::getValue);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package fr.titionfire.ffsaf.data.repository;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.TreeModel;
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class TreeRepository implements PanacheRepositoryBase<TreeModel, Long> {
|
||||
}
|
||||
@ -18,14 +18,13 @@ public class ClubEntity {
|
||||
private String name;
|
||||
private String clubId;
|
||||
private String country;
|
||||
private String shieldURL;
|
||||
private Map<Contact, String> contact;
|
||||
private String training_location;
|
||||
private String training_day_time;
|
||||
private String contact_intern;
|
||||
private String RNA;
|
||||
private String SIRET;
|
||||
private String no_affiliation;
|
||||
private Long SIRET;
|
||||
private Long no_affiliation;
|
||||
private boolean international;
|
||||
|
||||
public static ClubEntity fromModel (ClubModel model) {
|
||||
@ -38,7 +37,6 @@ public class ClubEntity {
|
||||
.name(model.getName())
|
||||
.clubId(model.getClubId())
|
||||
.country(model.getCountry())
|
||||
.shieldURL(model.getShieldURL())
|
||||
.contact(model.getContact())
|
||||
.training_location(model.getTraining_location())
|
||||
.training_day_time(model.getTraining_day_time())
|
||||
|
||||
@ -1,16 +1,26 @@
|
||||
package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.AffiliationRequestModel;
|
||||
import fr.titionfire.ffsaf.data.repository.AffiliationRequestRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
||||
import fr.titionfire.ffsaf.data.model.*;
|
||||
import fr.titionfire.ffsaf.data.repository.*;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleAffiliation;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleReqAffiliation;
|
||||
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||
import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
|
||||
import fr.titionfire.ffsaf.rest.from.AffiliationRequestSaveForm;
|
||||
import fr.titionfire.ffsaf.utils.SequenceType;
|
||||
import fr.titionfire.ffsaf.utils.Utils;
|
||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@WithSession
|
||||
@ApplicationScoped
|
||||
@ -20,41 +30,336 @@ public class AffiliationService {
|
||||
CombRepository combRepository;
|
||||
|
||||
@Inject
|
||||
AffiliationRequestRepository repository;
|
||||
ClubRepository clubRepository;
|
||||
|
||||
@Inject
|
||||
AffiliationRequestRepository repositoryRequest;
|
||||
|
||||
@Inject
|
||||
AffiliationRepository repository;
|
||||
|
||||
@Inject
|
||||
KeycloakService keycloakService;
|
||||
|
||||
@Inject
|
||||
SequenceRepository sequenceRepository;
|
||||
|
||||
@Inject
|
||||
LicenceRepository licenceRepository;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
public Uni<String> save(AffiliationRequestForm form) {
|
||||
public Uni<List<AffiliationRequestModel>> getAllReq() {
|
||||
return repositoryRequest.listAll();
|
||||
}
|
||||
|
||||
public Uni<AffiliationRequestModel> pre_save(AffiliationRequestForm form, boolean unique) {
|
||||
AffiliationRequestModel affModel = form.toModel();
|
||||
affModel.setSaison(Utils.getSaison());
|
||||
int currentSaison = Utils.getSaison();
|
||||
|
||||
return Uni.createFrom().item(affModel)
|
||||
.call(model -> ((model.getPresident_lincence() != 0) ? combRepository.find("licence",
|
||||
model.getPresident_lincence()).count().invoke(count -> {
|
||||
.invoke(Unchecked.consumer(model -> {
|
||||
if (model.getSaison() != currentSaison && model.getSaison() != currentSaison + 1) {
|
||||
throw new DBadRequestException("Saison non valid");
|
||||
}
|
||||
}))
|
||||
.chain(() -> repositoryRequest.count("siret = ?1 and saison = ?2", affModel.getSiret(),
|
||||
affModel.getSaison()))
|
||||
.onItem().invoke(Unchecked.consumer(count -> {
|
||||
if (count != 0 && unique) {
|
||||
throw new DBadRequestException("Demande d'affiliation déjà existante");
|
||||
}
|
||||
}))
|
||||
.chain(() -> clubRepository.find("SIRET = ?1", affModel.getSiret()).firstResult().chain(club ->
|
||||
repository.count("club = ?1 and saison = ?2", club, affModel.getSaison())))
|
||||
.onItem().invoke(Unchecked.consumer(count -> {
|
||||
if (count != 0) {
|
||||
throw new DBadRequestException("Affiliation déjà existante");
|
||||
}
|
||||
}))
|
||||
.map(o -> affModel)
|
||||
.call(model -> ((model.getM1_lincence() != -1) ? combRepository.find("licence",
|
||||
model.getM1_lincence()).count().invoke(Unchecked.consumer(count -> {
|
||||
if (count == 0) {
|
||||
throw new IllegalArgumentException("Licence président inconnue");
|
||||
throw new DBadRequestException("Licence membre n°1 inconnue");
|
||||
}
|
||||
}) : Uni.createFrom().nullItem())
|
||||
})) : Uni.createFrom().nullItem())
|
||||
)
|
||||
.call(model -> ((model.getTresorier_lincence() != 0) ? combRepository.find("licence",
|
||||
model.getTresorier_lincence()).count().invoke(count -> {
|
||||
.call(model -> ((model.getM2_lincence() != -1) ? combRepository.find("licence",
|
||||
model.getM2_lincence()).count().invoke(Unchecked.consumer(count -> {
|
||||
if (count == 0) {
|
||||
throw new IllegalArgumentException("Licence trésorier inconnue");
|
||||
throw new DBadRequestException("Licence membre n°2 inconnue");
|
||||
}
|
||||
}) : Uni.createFrom().nullItem())
|
||||
})) : Uni.createFrom().nullItem())
|
||||
)
|
||||
.call(model -> ((model.getSecretaire_lincence() != 0) ? combRepository.find("licence",
|
||||
model.getSecretaire_lincence()).count().invoke(count -> {
|
||||
.call(model -> ((model.getM3_lincence() != -1) ? combRepository.find("licence",
|
||||
model.getM3_lincence()).count().invoke(Unchecked.consumer(count -> {
|
||||
if (count == 0) {
|
||||
throw new IllegalArgumentException("Licence secrétaire inconnue");
|
||||
throw new DBadRequestException("Licence membre n°3 inconnue");
|
||||
}
|
||||
}) : Uni.createFrom().nullItem())
|
||||
).chain(model -> Panache.withTransaction(() -> repository.persist(model)))
|
||||
.call(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media,
|
||||
})) : Uni.createFrom().nullItem())
|
||||
);
|
||||
}
|
||||
|
||||
public Uni<?> saveEdit(AffiliationRequestForm form) {
|
||||
return pre_save(form, false)
|
||||
.chain(model -> repositoryRequest.findById(form.getId())
|
||||
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||
.chain(origine -> {
|
||||
origine.setName(model.getName());
|
||||
origine.setRNA(model.getRNA());
|
||||
origine.setAddress(model.getAddress());
|
||||
origine.setM1_lname(model.getM1_lname());
|
||||
origine.setM1_fname(model.getM1_fname());
|
||||
origine.setM1_lincence(model.getM1_lincence());
|
||||
origine.setM1_role(model.getM1_role());
|
||||
origine.setM1_email(model.getM1_email());
|
||||
origine.setM2_lname(model.getM2_lname());
|
||||
origine.setM2_fname(model.getM2_fname());
|
||||
origine.setM2_lincence(model.getM2_lincence());
|
||||
origine.setM2_role(model.getM2_role());
|
||||
origine.setM2_email(model.getM2_email());
|
||||
origine.setM3_lname(model.getM3_lname());
|
||||
origine.setM3_fname(model.getM3_fname());
|
||||
origine.setM3_lincence(model.getM3_lincence());
|
||||
origine.setM3_role(model.getM3_role());
|
||||
origine.setM3_email(model.getM3_email());
|
||||
|
||||
return Panache.withTransaction(() -> repositoryRequest.persist(origine));
|
||||
}));
|
||||
}
|
||||
|
||||
public Uni<String> save(AffiliationRequestForm form) {
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
return pre_save(form, true)
|
||||
.chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model)))
|
||||
.onItem()
|
||||
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media,
|
||||
"aff_request/logo")))
|
||||
.call(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media,
|
||||
.onItem()
|
||||
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media,
|
||||
"aff_request/status")))
|
||||
.map(__ -> "Ok");
|
||||
}
|
||||
|
||||
public Uni<?> saveAdmin(AffiliationRequestSaveForm form) {
|
||||
return repositoryRequest.findById(form.getId())
|
||||
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||
.map(model -> {
|
||||
model.setName(form.getName());
|
||||
model.setSiret(form.getSiret());
|
||||
model.setRNA(form.getRna());
|
||||
model.setAddress(form.getAddress());
|
||||
|
||||
if (form.getM1_mode() == 2) {
|
||||
model.setM1_lname(form.getM1_lname());
|
||||
model.setM1_fname(form.getM1_fname());
|
||||
} else {
|
||||
model.setM1_lincence(
|
||||
form.getM1_lincence() == null ? 0 : Integer.parseInt(form.getM1_lincence()));
|
||||
}
|
||||
model.setM1_role(form.getM1_role());
|
||||
if (form.getM1_email_mode() == 0)
|
||||
model.setM1_email(form.getM1_email());
|
||||
|
||||
if (form.getM2_mode() == 2) {
|
||||
model.setM2_lname(form.getM2_lname());
|
||||
model.setM2_fname(form.getM2_fname());
|
||||
} else {
|
||||
model.setM2_lincence(
|
||||
form.getM2_lincence() == null ? 0 : Integer.parseInt(form.getM2_lincence()));
|
||||
}
|
||||
model.setM2_role(form.getM2_role());
|
||||
if (form.getM2_email_mode() == 0)
|
||||
model.setM2_email(form.getM2_email());
|
||||
|
||||
if (form.getM3_mode() == 2) {
|
||||
model.setM3_lname(form.getM3_lname());
|
||||
model.setM3_fname(form.getM3_fname());
|
||||
} else {
|
||||
model.setM3_lincence(
|
||||
form.getM3_lincence() == null ? 0 : Integer.parseInt(form.getM3_lincence()));
|
||||
}
|
||||
model.setM3_role(form.getM3_role());
|
||||
if (form.getM3_email_mode() == 0)
|
||||
model.setM3_email(form.getM3_email());
|
||||
|
||||
return model;
|
||||
})
|
||||
.chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model)))
|
||||
.onItem()
|
||||
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media,
|
||||
"aff_request/logo")))
|
||||
.onItem()
|
||||
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getStatus(), media,
|
||||
"aff_request/status")))
|
||||
.map(__ -> "Ok");
|
||||
}
|
||||
|
||||
private Uni<?> setMembre(AffiliationRequestSaveForm.Member member, ClubModel club, int saison) {
|
||||
return Uni.createFrom().nullItem().chain(__ -> {
|
||||
if (member.getMode() == 2) {
|
||||
MembreModel membreModel = new MembreModel();
|
||||
membreModel.setFname(member.getFname());
|
||||
membreModel.setLname(member.getLname().toUpperCase());
|
||||
membreModel.setClub(club);
|
||||
membreModel.setRole(member.getRole());
|
||||
membreModel.setEmail(member.getEmail());
|
||||
return Panache.withTransaction(() ->
|
||||
combRepository.persist(membreModel)
|
||||
.chain(m -> sequenceRepository.getNextValueInTransaction(SequenceType.Licence)
|
||||
.invoke(l -> m.setLicence(Math.toIntExact(l)))
|
||||
.chain(() -> combRepository.persist(m))));
|
||||
} else {
|
||||
return combRepository.find("licence", Integer.parseInt(member.getLicence())).firstResult()
|
||||
.onItem().ifNull().switchTo(() -> {
|
||||
MembreModel membreModel = new MembreModel();
|
||||
membreModel.setFname(member.getFname());
|
||||
membreModel.setLname(member.getLname().toUpperCase());
|
||||
return Panache.withTransaction(
|
||||
() -> sequenceRepository.getNextValueInTransaction(SequenceType.Licence)
|
||||
.invoke(l -> membreModel.setLicence(Math.toIntExact(l)))
|
||||
.chain(() -> combRepository.persist(membreModel)));
|
||||
})
|
||||
.map(m -> {
|
||||
m.setClub(club);
|
||||
m.setRole(member.getRole());
|
||||
m.setEmail(member.getEmail());
|
||||
return m;
|
||||
}).call(m -> Panache.withTransaction(() -> combRepository.persist(m)));
|
||||
}
|
||||
})
|
||||
.call(m -> (m.getUserId() == null) ? keycloakService.initCompte(m.getId()) :
|
||||
keycloakService.setClubGroupMembre(m, club))
|
||||
.call(m -> Panache.withTransaction(() -> licenceRepository.persist(
|
||||
new LicenceModel(null, m, saison, null, true))));
|
||||
}
|
||||
|
||||
public Uni<?> accept(AffiliationRequestSaveForm form) {
|
||||
return repositoryRequest.findById(form.getId())
|
||||
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||
.chain(req ->
|
||||
clubRepository.find("SIRET = ?1", form.getSiret()).firstResult()
|
||||
.chain(model -> (model == null) ? acceptNew(form, req) : acceptOld(form, req, model))
|
||||
.call(club -> setMembre(form.new Member(1), club, req.getSaison())
|
||||
.call(__ -> setMembre(form.new Member(2), club, req.getSaison())
|
||||
.call(___ -> setMembre(form.new Member(3), club, req.getSaison()))))
|
||||
.onItem()
|
||||
.invoke(model -> Uni.createFrom()
|
||||
.future(Utils.replacePhoto(form.getId(), form.getLogo(), media,
|
||||
"aff_request/logo")))
|
||||
.onItem()
|
||||
.invoke(model -> Uni.createFrom()
|
||||
.future(Utils.replacePhoto(form.getId(), form.getStatus(), media,
|
||||
"aff_request/status")))
|
||||
.call(model -> Utils.moveMedia(form.getId(), model.getId(), media, "aff_request/logo",
|
||||
"ppClub"))
|
||||
.call(model -> Utils.moveMedia(form.getId(), model.getId(), media, "aff_request/status",
|
||||
"clubStatus"))
|
||||
)
|
||||
.map(__ -> "Ok");
|
||||
}
|
||||
|
||||
private Uni<ClubModel> acceptNew(AffiliationRequestSaveForm form, AffiliationRequestModel model) {
|
||||
return Uni.createFrom().nullItem()
|
||||
.chain(() -> {
|
||||
ClubModel club = new ClubModel();
|
||||
club.setName(form.getName());
|
||||
club.setCountry("fr");
|
||||
club.setSIRET(form.getSiret());
|
||||
club.setRNA(form.getRna());
|
||||
club.setAddress(form.getAddress());
|
||||
club.setAffiliations(List.of(new AffiliationModel(null, club, model.getSaison())));
|
||||
return Panache.withTransaction(() -> clubRepository.persist(club)
|
||||
.chain(c -> sequenceRepository.getNextValueInTransaction(SequenceType.Affiliation)
|
||||
.invoke(c::setNo_affiliation)
|
||||
.chain(() -> clubRepository.persist(c))
|
||||
.chain(() -> repositoryRequest.delete(model))
|
||||
)
|
||||
.chain(() -> repository.persist(new AffiliationModel(null, club, model.getSaison())))
|
||||
.map(c -> club));
|
||||
});
|
||||
}
|
||||
|
||||
private Uni<ClubModel> acceptOld(AffiliationRequestSaveForm form, AffiliationRequestModel model, ClubModel club) {
|
||||
return Uni.createFrom().nullItem()
|
||||
.chain(() -> {
|
||||
club.setName(form.getName());
|
||||
club.setCountry("fr");
|
||||
club.setSIRET(form.getSiret());
|
||||
club.setRNA(form.getRna());
|
||||
club.setAddress(form.getAddress());
|
||||
return Panache.withTransaction(() -> clubRepository.persist(club)
|
||||
.chain(() -> repository.persist(new AffiliationModel(null, club, model.getSaison())))
|
||||
.chain(() -> repositoryRequest.delete(model)));
|
||||
})
|
||||
.map(__ -> club);
|
||||
}
|
||||
|
||||
public Uni<SimpleReqAffiliation> getRequest(long id) {
|
||||
return repositoryRequest.findById(id).map(SimpleReqAffiliation::fromModel)
|
||||
.onItem().ifNull().failWith(new DNotFoundException("Demande d'affiliation non trouvé"))
|
||||
.call(out -> clubRepository.find("SIRET = ?1", out.getSiret()).firstResult().invoke(c -> {
|
||||
if (c != null) {
|
||||
out.setClub(c.getId());
|
||||
out.setClub_name(c.getName());
|
||||
out.setClub_no_aff(c.getNo_affiliation());
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public Uni<List<SimpleAffiliation>> getCurrentSaisonAffiliation() {
|
||||
return repositoryRequest.list("saison = ?1 or saison = ?1 + 1", Utils.getSaison())
|
||||
.map(models -> models.stream()
|
||||
.map(model -> new SimpleAffiliation(model.getId() * -1, model.getSiret(), model.getSaison(),
|
||||
false)).toList())
|
||||
.chain(aff -> repository.list("saison = ?1", Utils.getSaison())
|
||||
.map(models -> models.stream().map(SimpleAffiliation::fromModel).toList())
|
||||
.map(aff2 -> Stream.concat(aff2.stream(), aff.stream()).toList())
|
||||
);
|
||||
}
|
||||
|
||||
public Uni<List<SimpleAffiliation>> getAffiliation(long id) {
|
||||
return clubRepository.findById(id)
|
||||
.onItem().ifNull().failWith(new DNotFoundException("Club non trouvé"))
|
||||
.call(model -> Mutiny.fetch(model.getAffiliations()))
|
||||
.chain(model -> repositoryRequest.list("siret = ?1", model.getSIRET())
|
||||
.map(reqs -> reqs.stream().map(req ->
|
||||
new SimpleAffiliation(req.getId() * -1, model.getId(), req.getSaison(), false)))
|
||||
.map(aff2 -> Stream.concat(aff2,
|
||||
model.getAffiliations().stream().map(SimpleAffiliation::fromModel)).toList())
|
||||
);
|
||||
}
|
||||
|
||||
public Uni<SimpleAffiliation> 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");
|
||||
}
|
||||
}))
|
||||
.chain(club ->
|
||||
Panache.withTransaction(() -> repository.persist(new AffiliationModel(null, club, saison))
|
||||
.chain(c -> (club.getNo_affiliation() != null) ? Uni.createFrom().item(c) :
|
||||
sequenceRepository.getNextValueInTransaction(SequenceType.Affiliation)
|
||||
.invoke(club::setNo_affiliation)
|
||||
.chain(() -> clubRepository.persist(club))
|
||||
.map(o -> c)
|
||||
)))
|
||||
.map(SimpleAffiliation::fromModel);
|
||||
}
|
||||
|
||||
public Uni<?> deleteAffiliation(long id) {
|
||||
return Panache.withTransaction(() -> repository.deleteById(id));
|
||||
}
|
||||
|
||||
public Uni<?> deleteReqAffiliation(long id) {
|
||||
return Panache.withTransaction(() -> repositoryRequest.deleteById(id))
|
||||
.call(__ -> Utils.deleteMedia(id, media, "aff_request/logo"))
|
||||
.call(__ -> Utils.deleteMedia(id, media, "aff_request/status"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,44 @@
|
||||
package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import fr.titionfire.ffsaf.data.model.AffiliationModel;
|
||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.data.repository.ClubRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
||||
import fr.titionfire.ffsaf.net2.ServerCustom;
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
|
||||
import fr.titionfire.ffsaf.net2.request.SReqClub;
|
||||
import fr.titionfire.ffsaf.rest.data.DeskMember;
|
||||
import fr.titionfire.ffsaf.rest.data.RenewAffData;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleClubList;
|
||||
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||
import fr.titionfire.ffsaf.rest.from.FullClubForm;
|
||||
import fr.titionfire.ffsaf.rest.from.PartClubForm;
|
||||
import fr.titionfire.ffsaf.utils.*;
|
||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheQuery;
|
||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import io.quarkus.vertx.VertxContextSupport;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
|
||||
|
||||
@WithSession
|
||||
@ApplicationScoped
|
||||
@ -20,13 +47,27 @@ public class ClubService {
|
||||
@Inject
|
||||
ClubRepository repository;
|
||||
|
||||
@Inject
|
||||
ServerCustom serverCustom;
|
||||
|
||||
@Inject
|
||||
CombRepository combRepository;
|
||||
|
||||
@Inject
|
||||
KeycloakService keycloakService;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
public SimpleClubModel findByIdOptionalClub(long id) throws Throwable {
|
||||
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel)));
|
||||
return VertxContextSupport.subscribeAndAwait(
|
||||
() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleClubModel::fromModel)));
|
||||
}
|
||||
|
||||
public Collection<SimpleClubModel> findAllClub() throws Throwable {
|
||||
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(
|
||||
() -> repository.findAll().list().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList())));
|
||||
() -> repository.findAll().list()
|
||||
.map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList())));
|
||||
}
|
||||
|
||||
public Uni<List<ClubModel>> getAll() {
|
||||
@ -39,4 +80,203 @@ public class ClubService {
|
||||
return Panache.withTransaction(() -> repository.persist(clubModel));
|
||||
});
|
||||
}
|
||||
|
||||
public Uni<PageResult<SimpleClubList>> search(Integer limit, int page, String search, String country) {
|
||||
if (search == null)
|
||||
search = "";
|
||||
search = search + "%";
|
||||
|
||||
PanacheQuery<ClubModel> query;
|
||||
|
||||
if (country == null || country.isBlank())
|
||||
query = repository.find("name LIKE ?1",
|
||||
Sort.ascending("name"), search).page(Page.ofSize(limit));
|
||||
else
|
||||
query = repository.find("name LIKE ?1 AND country LIKE ?2",
|
||||
Sort.ascending("name"), search, country + "%").page(Page.ofSize(limit));
|
||||
return getPageResult(query, limit, page);
|
||||
}
|
||||
|
||||
private Uni<PageResult<SimpleClubList>> getPageResult(PanacheQuery<ClubModel> query, int limit, int page) {
|
||||
return Uni.createFrom().item(new PageResult<SimpleClubList>())
|
||||
.invoke(result -> result.setPage(page))
|
||||
.invoke(result -> result.setPage_size(limit))
|
||||
.call(result -> query.count().invoke(result::setResult_count))
|
||||
.call(result -> query.pageCount()
|
||||
.invoke(Unchecked.consumer(pages -> {
|
||||
if (page > pages) throw new DBadRequestException("Page out of range");
|
||||
}))
|
||||
.invoke(result::setPage_count))
|
||||
.call(result -> query.page(Page.of(page, limit)).list()
|
||||
.map(membreModels -> membreModels.stream().map(SimpleClubList::fromModel).toList())
|
||||
.invoke(result::setResult));
|
||||
}
|
||||
|
||||
public Uni<ClubModel> getById(long id) {
|
||||
return repository.findById(id).call(m -> Mutiny.fetch(m.getContact()));
|
||||
}
|
||||
|
||||
public Uni<ClubModel> getByClubId(String clubId) {
|
||||
return repository.find("clubId", clubId).firstResult();
|
||||
}
|
||||
|
||||
public Uni<ClubModel> getOfUser(SecurityCtx securityCtx) {
|
||||
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult().invoke(Unchecked.consumer(m -> {
|
||||
if (m == null || m.getClub() == null)
|
||||
throw new DNotFoundException("Club non trouvé");
|
||||
}))
|
||||
.map(MembreModel::getClub)
|
||||
.call(club -> Mutiny.fetch(club.getContact()));
|
||||
}
|
||||
|
||||
public Uni<List<DeskMember>> getClubDesk(Consumer<ClubModel> consumer, long id) {
|
||||
return repository.findById(id).invoke(consumer)
|
||||
.chain(club -> combRepository.list("club = ?1", club))
|
||||
.map(combs -> combs.stream()
|
||||
.filter(o -> o.getRole() != null && o.getRole().level >= RoleAsso.MEMBREBUREAU.level)
|
||||
.sorted((o1, o2) -> o2.getRole().level - o1.getRole().level)
|
||||
.map(DeskMember::fromModel)
|
||||
.toList());
|
||||
}
|
||||
|
||||
public Uni<String> updateOfUser(SecurityCtx securityCtx, PartClubForm form) {
|
||||
TypeReference<HashMap<Contact, String>> typeRef = new TypeReference<>() {
|
||||
};
|
||||
|
||||
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult().invoke(Unchecked.consumer(m -> {
|
||||
if (m == null || m.getClub() == null)
|
||||
throw new DNotFoundException("Club non trouvé");
|
||||
if (!securityCtx.isInClubGroup(m.getClub().getId()))
|
||||
throw new DForbiddenException();
|
||||
}))
|
||||
.map(MembreModel::getClub)
|
||||
.call(club -> Mutiny.fetch(club.getContact()))
|
||||
.chain(Unchecked.function(club -> {
|
||||
club.setContact_intern(form.getContact_intern());
|
||||
club.setAddress(form.getAddress());
|
||||
|
||||
try {
|
||||
club.setContact(MAPPER.readValue(form.getContact(), typeRef));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new DBadRequestException("Erreur de format des contacts");
|
||||
}
|
||||
|
||||
club.setTraining_location(form.getTraining_location());
|
||||
club.setTraining_day_time(form.getTraining_day_time());
|
||||
return Panache.withTransaction(() -> repository.persist(club));
|
||||
}))
|
||||
.map(__ -> "OK");
|
||||
}
|
||||
|
||||
public Uni<String> update(long id, FullClubForm input) {
|
||||
return repository.findById(id).call(m -> Mutiny.fetch(m.getContact()))
|
||||
.onItem().transformToUni(Unchecked.function(m -> {
|
||||
TypeReference<HashMap<Contact, String>> typeRef = new TypeReference<>() {
|
||||
};
|
||||
|
||||
m.setName(input.getName());
|
||||
m.setCountry(input.getCountry());
|
||||
m.setInternational(input.isInternational());
|
||||
|
||||
if (!input.isInternational()) {
|
||||
m.setTraining_location(input.getTraining_location());
|
||||
m.setTraining_day_time(input.getTraining_day_time());
|
||||
m.setContact_intern(input.getContact_intern());
|
||||
m.setRNA(input.getRna());
|
||||
if (input.getSiret() != null && !input.getSiret().isBlank())
|
||||
m.setSIRET(Long.parseLong(input.getSiret()));
|
||||
m.setAddress(input.getAddress());
|
||||
|
||||
try {
|
||||
m.setContact(MAPPER.readValue(input.getContact(), typeRef));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new DBadRequestException("Erreur de format des contacts");
|
||||
}
|
||||
}
|
||||
return Panache.withTransaction(() -> repository.persist(m));
|
||||
}))
|
||||
.invoke(membreModel -> SReqClub.sendIfNeed(serverCustom.clients,
|
||||
SimpleClubModel.fromModel(membreModel)))
|
||||
.map(__ -> "OK");
|
||||
}
|
||||
|
||||
public Uni<Long> add(FullClubForm input) {
|
||||
TypeReference<HashMap<Contact, String>> typeRef = new TypeReference<>() {
|
||||
};
|
||||
|
||||
return Uni.createFrom().nullItem()
|
||||
.chain(() -> {
|
||||
ClubModel clubModel = new ClubModel();
|
||||
|
||||
clubModel.setName(input.getName());
|
||||
clubModel.setCountry(input.getCountry());
|
||||
clubModel.setInternational(input.isInternational());
|
||||
clubModel.setNo_affiliation(null);
|
||||
if (!input.isInternational()) {
|
||||
clubModel.setTraining_location(input.getTraining_location());
|
||||
clubModel.setTraining_day_time(input.getTraining_day_time());
|
||||
clubModel.setContact_intern(input.getContact_intern());
|
||||
clubModel.setRNA(input.getRna());
|
||||
if (input.getSiret() != null && !input.getSiret().isBlank())
|
||||
clubModel.setSIRET(Long.parseLong(input.getSiret()));
|
||||
clubModel.setAddress(input.getAddress());
|
||||
|
||||
try {
|
||||
clubModel.setContact(MAPPER.readValue(input.getContact(), typeRef));
|
||||
} catch (JsonProcessingException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return Panache.withTransaction(() -> repository.persist(clubModel));
|
||||
})
|
||||
.call(clubModel -> keycloakService.getGroupFromClub(clubModel)) // create group in keycloak
|
||||
.invoke(clubModel -> SReqClub.sendAddIfNeed(serverCustom.clients, SimpleClubModel.fromModel(clubModel)))
|
||||
.map(ClubModel::getId);
|
||||
}
|
||||
|
||||
public Uni<?> delete(long id) {
|
||||
return repository.findById(id)
|
||||
.chain(club -> combRepository.list("club = ?1", club)
|
||||
.map(combModels -> combModels.stream().peek(combModel -> {
|
||||
combModel.setClub(null);
|
||||
combModel.setRole(RoleAsso.MEMBRE);
|
||||
}).toList())
|
||||
.call(list -> (list.isEmpty()) ? Uni.createFrom().voidItem() :
|
||||
Uni.join().all(list.stream().filter(m -> m.getUserId() != null)
|
||||
.map(m -> keycloakService.clearUser(m.getUserId())).toList())
|
||||
.andCollectFailures())
|
||||
.chain(list -> Panache.withTransaction(() -> combRepository.persist(list)))
|
||||
.map(o -> club)
|
||||
)
|
||||
.call(clubModel -> (clubModel.getClubId() == null) ? Uni.createFrom()
|
||||
.voidItem() : keycloakService.removeClubGroup(clubModel.getClubId()))
|
||||
.invoke(membreModel -> SReqClub.sendRmIfNeed(serverCustom.clients, id))
|
||||
.chain(clubModel -> Panache.withTransaction(() -> repository.delete(clubModel)))
|
||||
.call(__ -> Utils.deleteMedia(id, media, "ppClub"))
|
||||
.call(__ -> Utils.deleteMedia(id, media, "clubStatus"));
|
||||
}
|
||||
|
||||
public Uni<RenewAffData> getRenewData(long id, List<Long> mIds) {
|
||||
RenewAffData data = new RenewAffData();
|
||||
|
||||
return repository.findById(id)
|
||||
.call(clubModel -> Mutiny.fetch(clubModel.getAffiliations()))
|
||||
.invoke(clubModel -> {
|
||||
data.setName(clubModel.getName());
|
||||
data.setSiret(clubModel.getSIRET());
|
||||
data.setRna(clubModel.getRNA());
|
||||
data.setAddress(clubModel.getAddress());
|
||||
data.setSaison(
|
||||
clubModel.getAffiliations().stream().max(Comparator.comparing(AffiliationModel::getSaison))
|
||||
.map(AffiliationModel::getSaison).map(i -> Math.min(i + 1, Utils.getSaison() + 1))
|
||||
.orElse(Utils.getSaison()));
|
||||
})
|
||||
.chain(club -> combRepository.list("id IN ?1", mIds))
|
||||
.invoke(combs -> data.setMembers(combs.stream()
|
||||
.filter(o -> o.getRole() != null && o.getRole().level >= RoleAsso.MEMBREBUREAU.level)
|
||||
.sorted((o1, o2) -> o2.getRole().level - o1.getRole().level)
|
||||
.map(RenewAffData.RenewMember::new)
|
||||
.toList()))
|
||||
.map(o -> data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,134 @@
|
||||
package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.CompetitionModel;
|
||||
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
|
||||
import fr.titionfire.ffsaf.net2.ServerCustom;
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleCompet;
|
||||
import fr.titionfire.ffsaf.net2.request.SReqCompet;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import io.quarkus.cache.Cache;
|
||||
import io.quarkus.cache.CacheName;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@ApplicationScoped
|
||||
public class CompetPermService {
|
||||
|
||||
@Inject
|
||||
ServerCustom serverCustom;
|
||||
|
||||
@Inject
|
||||
@CacheName("safca-config")
|
||||
Cache cache;
|
||||
|
||||
@Inject
|
||||
@CacheName("safca-have-access")
|
||||
Cache cacheAccess;
|
||||
|
||||
@Inject
|
||||
CompetitionRepository competitionRepository;
|
||||
|
||||
public Uni<SimpleCompet> getSafcaConfig(long id) {
|
||||
return cache.get(id, k -> {
|
||||
CompletableFuture<SimpleCompet> f = new CompletableFuture<>();
|
||||
SReqCompet.getConfig(serverCustom.clients, id, f);
|
||||
System.out.println("get config");
|
||||
try {
|
||||
return f.get(1500, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Uni<HashMap<Long, String>> getAllHaveAccess(String subject) {
|
||||
return cacheAccess.get(subject, k -> {
|
||||
CompletableFuture<HashMap<Long, String>> f = new CompletableFuture<>();
|
||||
SReqCompet.getAllHaveAccess(serverCustom.clients, subject, f);
|
||||
System.out.println("get all have access");
|
||||
try {
|
||||
return f.get(1500, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
|
||||
return hasViewPerm(securityCtx, Uni.createFrom().item(competitionModel));
|
||||
}
|
||||
|
||||
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, long id) {
|
||||
return hasViewPerm(securityCtx, competitionRepository.findById(id));
|
||||
}
|
||||
|
||||
private Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
|
||||
return in.call(o -> (
|
||||
securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin")) ?
|
||||
Uni.createFrom().nullItem()
|
||||
:
|
||||
o.getSystem() == CompetitionSystem.SAFCA ?
|
||||
hasSafcaViewPerm(securityCtx, o.getId())
|
||||
: Uni.createFrom().nullItem().invoke(Unchecked.consumer(__ -> {
|
||||
if (!securityCtx.isInClubGroup(o.getClub().getId()))
|
||||
throw new DForbiddenException();
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
|
||||
return hasEditPerm(securityCtx, Uni.createFrom().item(competitionModel));
|
||||
}
|
||||
|
||||
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, long id) {
|
||||
return hasEditPerm(securityCtx, competitionRepository.findById(id));
|
||||
}
|
||||
|
||||
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
|
||||
return in.call(o -> (
|
||||
securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin")) ?
|
||||
Uni.createFrom().nullItem()
|
||||
:
|
||||
o.getSystem() == CompetitionSystem.SAFCA ?
|
||||
hasSafcaEditPerm(securityCtx, o.getId())
|
||||
: Uni.createFrom().nullItem().invoke(Unchecked.consumer(__ -> {
|
||||
if (!securityCtx.isInClubGroup(o.getClub().getId()))
|
||||
throw new DForbiddenException();
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
private Uni<?> hasSafcaViewPerm(SecurityCtx securityCtx, long id) {
|
||||
return securityCtx.roleHas("safca_super_admin") ?
|
||||
Uni.createFrom().nullItem()
|
||||
:
|
||||
getSafcaConfig(id).chain(Unchecked.function(o -> {
|
||||
if (!o.admin().contains(UUID.fromString(securityCtx.getSubject())) && !o.table()
|
||||
.contains(UUID.fromString(securityCtx.getSubject())))
|
||||
throw new DForbiddenException();
|
||||
return Uni.createFrom().nullItem();
|
||||
}));
|
||||
}
|
||||
|
||||
private Uni<?> hasSafcaEditPerm(SecurityCtx securityCtx, long id) {
|
||||
return securityCtx.roleHas("safca_super_admin") ?
|
||||
Uni.createFrom().nullItem()
|
||||
:
|
||||
getSafcaConfig(id).chain(Unchecked.function(o -> {
|
||||
if (!o.admin().contains(UUID.fromString(securityCtx.getSubject())))
|
||||
throw new DForbiddenException();
|
||||
return Uni.createFrom().nullItem();
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,248 @@
|
||||
package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.CompetitionModel;
|
||||
import fr.titionfire.ffsaf.data.repository.ClubRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.MatchRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.PouleRepository;
|
||||
import fr.titionfire.ffsaf.net2.ServerCustom;
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleCompet;
|
||||
import fr.titionfire.ffsaf.net2.request.SReqCompet;
|
||||
import fr.titionfire.ffsaf.rest.data.CompetitionData;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleCompetData;
|
||||
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||
import io.vertx.mutiny.core.Vertx;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@WithSession
|
||||
@ApplicationScoped
|
||||
public class CompetitionService {
|
||||
|
||||
@Inject
|
||||
CompetitionRepository repository;
|
||||
|
||||
@Inject
|
||||
PouleRepository pouleRepository;
|
||||
|
||||
@Inject
|
||||
MatchRepository matchRepository;
|
||||
|
||||
@Inject
|
||||
KeycloakService keycloakService;
|
||||
|
||||
@Inject
|
||||
ServerCustom serverCustom;
|
||||
|
||||
@Inject
|
||||
CompetPermService permService;
|
||||
|
||||
@Inject
|
||||
Vertx vertx;
|
||||
|
||||
public Uni<CompetitionData> getById(SecurityCtx securityCtx, Long id) {
|
||||
if (id == 0) {
|
||||
return Uni.createFrom()
|
||||
.item(new CompetitionData(null, "", "", new Date(), CompetitionSystem.SAFCA,
|
||||
null, "", ""));
|
||||
}
|
||||
return permService.hasViewPerm(securityCtx, id)
|
||||
.map(CompetitionData::fromModel)
|
||||
.chain(data ->
|
||||
vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
keycloakService.getUser(UUID.fromString(data.getOwner()))
|
||||
.ifPresent(user -> data.setOwner(user.getUsername()));
|
||||
return data;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public Uni<List<CompetitionData>> getAll(SecurityCtx securityCtx) {
|
||||
return repository.listAll()
|
||||
.chain(o ->
|
||||
permService.getAllHaveAccess(securityCtx.getSubject())
|
||||
.chain(map -> Uni.createFrom().item(o.stream()
|
||||
.filter(p -> {
|
||||
if (securityCtx.getSubject().equals(p.getOwner()))
|
||||
return true;
|
||||
if (p.getSystem() == CompetitionSystem.SAFCA) {
|
||||
if (map.containsKey(p.getId()))
|
||||
return map.get(p.getId()).equals("admin");
|
||||
return securityCtx.roleHas("federation_admin")
|
||||
|| securityCtx.roleHas("safca_super_admin");
|
||||
}
|
||||
return securityCtx.roleHas("federation_admin");
|
||||
})
|
||||
.map(CompetitionData::fromModel).toList())
|
||||
));
|
||||
}
|
||||
|
||||
public Uni<List<CompetitionData>> getAllSystem(SecurityCtx securityCtx,
|
||||
CompetitionSystem system) {
|
||||
if (system == CompetitionSystem.SAFCA) {
|
||||
return permService.getAllHaveAccess(securityCtx.getSubject())
|
||||
.chain(map ->
|
||||
repository.list("system = ?1", system)
|
||||
.map(data -> data.stream()
|
||||
.filter(p -> {
|
||||
if (securityCtx.getSubject().equals(p.getOwner()))
|
||||
return true;
|
||||
if (map.containsKey(p.getId()))
|
||||
return map.get(p.getId()).equals("admin");
|
||||
return securityCtx.roleHas("federation_admin")
|
||||
|| securityCtx.roleHas("safca_super_admin");
|
||||
})
|
||||
.map(CompetitionData::fromModel).toList())
|
||||
);
|
||||
}
|
||||
|
||||
return repository.list("system = ?1", system)
|
||||
.map(data -> data.stream()
|
||||
.filter(p -> {
|
||||
if (securityCtx.getSubject().equals(p.getOwner()))
|
||||
return true;
|
||||
return securityCtx.roleHas("federation_admin") ||
|
||||
securityCtx.isInClubGroup(p.getClub().getId());
|
||||
})
|
||||
.map(CompetitionData::fromModel).toList());
|
||||
}
|
||||
|
||||
public Uni<CompetitionData> addOrUpdate(SecurityCtx securityCtx, CompetitionData data) {
|
||||
if (data.getId() == null) {
|
||||
return new ClubRepository().findById(data.getClub()).invoke(Unchecked.consumer(clubModel -> {
|
||||
if (!securityCtx.isInClubGroup(clubModel.getId()))
|
||||
throw new DForbiddenException();
|
||||
})) // TODO check if user can create competition
|
||||
.chain(clubModel -> {
|
||||
CompetitionModel model = new CompetitionModel();
|
||||
|
||||
model.setId(null);
|
||||
model.setSystem(data.getSystem());
|
||||
model.setClub(clubModel);
|
||||
model.setDate(data.getDate());
|
||||
model.setInsc(new ArrayList<>());
|
||||
model.setUuid(UUID.randomUUID().toString());
|
||||
model.setName(data.getName());
|
||||
model.setOwner(securityCtx.getSubject());
|
||||
|
||||
return Panache.withTransaction(() -> repository.persist(model));
|
||||
}).map(CompetitionData::fromModel)
|
||||
.call(__ -> permService.cacheAccess.invalidate(securityCtx.getSubject()));
|
||||
} else {
|
||||
return permService.hasEditPerm(securityCtx, data.getId())
|
||||
.chain(model -> {
|
||||
model.setDate(data.getDate());
|
||||
model.setName(data.getName());
|
||||
|
||||
return vertx.getOrCreateContext().executeBlocking(() ->
|
||||
keycloakService.getUser(data.getOwner()).map(UserRepresentation::getId).orElse(null))
|
||||
.invoke(Unchecked.consumer(newOwner -> {
|
||||
if (newOwner == null)
|
||||
throw new DBadRequestException("User " + data.getOwner() + " not found");
|
||||
if (!newOwner.equals(model.getOwner())) {
|
||||
if (!securityCtx.roleHas("federation_admin")
|
||||
&& !securityCtx.roleHas("safca_super_admin")
|
||||
&& !securityCtx.getSubject().equals(model.getOwner()))
|
||||
throw new DForbiddenException();
|
||||
model.setOwner(newOwner);
|
||||
}
|
||||
}))
|
||||
.chain(__ -> Panache.withTransaction(() -> repository.persist(model)));
|
||||
}).map(CompetitionData::fromModel)
|
||||
.call(__ -> permService.cacheAccess.invalidate(securityCtx.getSubject()));
|
||||
}
|
||||
}
|
||||
|
||||
public Uni<?> delete(SecurityCtx securityCtx, Long id) {
|
||||
return repository.findById(id).invoke(Unchecked.consumer(c -> {
|
||||
if (!securityCtx.getSubject().equals(c.getOwner()) || securityCtx.roleHas("federation_admin"))
|
||||
throw new DForbiddenException();
|
||||
}))
|
||||
.call(competitionModel -> pouleRepository.list("compet = ?1", competitionModel)
|
||||
.call(pouleModels -> Uni.join()
|
||||
.all(pouleModels.stream()
|
||||
.map(pouleModel -> Panache.withTransaction(
|
||||
() -> matchRepository.delete("poule = ?1", pouleModel.getId())))
|
||||
.toList())
|
||||
.andCollectFailures()))
|
||||
.call(competitionModel -> Panache.withTransaction(
|
||||
() -> pouleRepository.delete("compet = ?1", competitionModel)))
|
||||
.chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId())))
|
||||
.invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id))
|
||||
.call(__ -> permService.cache.invalidate(id));
|
||||
}
|
||||
|
||||
public Uni<SimpleCompetData> getSafcaData(SecurityCtx securityCtx, Long id) {
|
||||
return permService.getSafcaConfig(id)
|
||||
.call(Unchecked.function(o -> {
|
||||
if (!securityCtx.getSubject().equals(o.owner())
|
||||
&& !securityCtx.roleHas("federation_admin")
|
||||
&& !securityCtx.roleHas("safca_super_admin")
|
||||
&& !o.admin().contains(UUID.fromString(securityCtx.getSubject()))
|
||||
&& !o.table().contains(UUID.fromString(securityCtx.getSubject())))
|
||||
throw new DForbiddenException();
|
||||
return Uni.createFrom().nullItem();
|
||||
}))
|
||||
.chain(simpleCompet -> {
|
||||
SimpleCompetData data = SimpleCompetData.fromModel(simpleCompet);
|
||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
data.setAdmin(simpleCompet.admin().stream().map(uuid -> keycloakService.getUser(uuid))
|
||||
.filter(Optional::isPresent)
|
||||
.map(user -> user.get().getUsername())
|
||||
.toList());
|
||||
data.setTable(simpleCompet.table().stream().map(uuid -> keycloakService.getUser(uuid))
|
||||
.filter(Optional::isPresent)
|
||||
.map(user -> user.get().getUsername())
|
||||
.toList());
|
||||
|
||||
return data;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public Uni<?> setSafcaData(SecurityCtx securityCtx, SimpleCompetData data) {
|
||||
return permService.hasEditPerm(securityCtx, data.getId())
|
||||
.chain(__ -> vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
ArrayList<UUID> admin = new ArrayList<>();
|
||||
ArrayList<UUID> table = new ArrayList<>();
|
||||
for (String username : data.getAdmin()) {
|
||||
Optional<UserRepresentation> opt = keycloakService.getUser(username);
|
||||
if (opt.isEmpty())
|
||||
throw new DBadRequestException("User " + username + " not found");
|
||||
admin.add(UUID.fromString(opt.get().getId()));
|
||||
}
|
||||
for (String username : data.getTable()) {
|
||||
Optional<UserRepresentation> opt = keycloakService.getUser(username);
|
||||
if (opt.isEmpty())
|
||||
throw new DBadRequestException("User " + username + " not found");
|
||||
table.add(UUID.fromString(opt.get().getId()));
|
||||
}
|
||||
|
||||
return new SimpleCompet(data.getId(), "", data.isShow_blason(),
|
||||
data.isShow_flag(), admin, table);
|
||||
}))
|
||||
.invoke(simpleCompet -> SReqCompet.sendUpdate(serverCustom.clients, simpleCompet))
|
||||
.call(simpleCompet -> permService.getSafcaConfig(data.getId())
|
||||
.call(c -> Uni.join().all(Stream.concat(
|
||||
Stream.concat(
|
||||
c.admin().stream().filter(uuid -> !simpleCompet.admin().contains(uuid)),
|
||||
simpleCompet.admin().stream().filter(uuid -> !c.admin().contains(uuid))),
|
||||
Stream.concat(
|
||||
c.table().stream().filter(uuid -> !simpleCompet.table().contains(uuid)),
|
||||
simpleCompet.table().stream().filter(uuid -> !c.table().contains(uuid))))
|
||||
.map(uuid -> permService.cacheAccess.invalidate(uuid.toString())).toList())
|
||||
.andCollectFailures()))
|
||||
.call(__ -> permService.cache.invalidate(data.getId()));
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||
import fr.titionfire.ffsaf.utils.*;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
@ -23,6 +24,7 @@ import java.text.Normalizer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@ApplicationScoped
|
||||
public class KeycloakService {
|
||||
@ -49,21 +51,26 @@ public class KeycloakService {
|
||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
GroupRepresentation clubGroup =
|
||||
keycloak.realm(realm).groups().groups().stream().filter(g -> g.getName().equals("club"))
|
||||
.findAny().orElseThrow(() -> new KeycloakException("Fail to fetch group %s".formatted("club")));
|
||||
.findAny()
|
||||
.orElseThrow(() -> new KeycloakException("Fail to fetch group %s".formatted("club")));
|
||||
|
||||
GroupRepresentation groupRepresentation = new GroupRepresentation();
|
||||
groupRepresentation.setName(club.getId() + "-" + club.getName());
|
||||
|
||||
try (Response response =
|
||||
keycloak.realm(realm).groups().group(clubGroup.getId()).subGroup(groupRepresentation)) {
|
||||
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo().equals(Response.Status.CONFLICT))
|
||||
throw new KeycloakException("Fail to set group parent for club: %s (reason=%s)".formatted(club.getName(),
|
||||
response.getStatusInfo().getReasonPhrase()));
|
||||
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo()
|
||||
.equals(Response.Status.CONFLICT))
|
||||
throw new KeycloakException(
|
||||
"Fail to set group parent for club: %s (reason=%s)".formatted(club.getName(),
|
||||
response.getStatusInfo().getReasonPhrase()));
|
||||
}
|
||||
|
||||
return keycloak.realm(realm).groups().group(clubGroup.getId()).getSubGroups(0, 1000, true).stream()
|
||||
.filter(g -> g.getName().startsWith(club.getId() + "-")).findAny().map(GroupRepresentation::getId)
|
||||
.orElseThrow(() -> new KeycloakException("Fail to fetch group %s*".formatted(club.getId() + "-")));
|
||||
.filter(g -> g.getName().startsWith(club.getId() + "-")).findAny()
|
||||
.map(GroupRepresentation::getId)
|
||||
.orElseThrow(
|
||||
() -> new KeycloakException("Fail to fetch group %s*".formatted(club.getId() + "-")));
|
||||
}
|
||||
).call(id -> clubService.setClubId(club.getId(), id));
|
||||
}
|
||||
@ -72,21 +79,24 @@ public class KeycloakService {
|
||||
|
||||
public Uni<String> getUserFromMember(MembreModel membreModel) {
|
||||
if (membreModel.getUserId() == null) {
|
||||
return Uni.createFrom().failure(new NullPointerException("No keycloak user linked to the user id=" + membreModel.getId()));
|
||||
return Uni.createFrom()
|
||||
.failure(new DInternalError("No keycloak user linked to the user id=" + membreModel.getId()));
|
||||
}
|
||||
return Uni.createFrom().item(membreModel::getUserId);
|
||||
}
|
||||
|
||||
public Uni<String> setClubGroupMembre(MembreModel membreModel, ClubModel club) {
|
||||
return getGroupFromClub(club).chain(
|
||||
clubId -> getUserFromMember(membreModel).chain(userId -> vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
UserResource user = keycloak.realm(realm).users().get(userId);
|
||||
user.groups().stream().filter(g -> g.getPath().startsWith("/club")).forEach(g -> user.leaveGroup(g.getId()));
|
||||
user.joinGroup(clubId);
|
||||
LOGGER.infof("Set club \"%s\" to user %s (%s)", club.getName(), userId,
|
||||
user.toRepresentation().getUsername());
|
||||
return "OK";
|
||||
})));
|
||||
clubId -> getUserFromMember(membreModel).chain(
|
||||
userId -> vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
UserResource user = keycloak.realm(realm).users().get(userId);
|
||||
user.groups().stream().filter(g -> g.getPath().startsWith("/club"))
|
||||
.forEach(g -> user.leaveGroup(g.getId()));
|
||||
user.joinGroup(clubId);
|
||||
LOGGER.infof("Set club \"%s\" to user %s (%s)", club.getName(), userId,
|
||||
user.toRepresentation().getUsername());
|
||||
return "OK";
|
||||
})));
|
||||
}
|
||||
|
||||
public Uni<?> setEmail(String userId, String email) {
|
||||
@ -104,13 +114,14 @@ public class KeycloakService {
|
||||
|
||||
public Uni<?> setAutoRoleMembre(String id, RoleAsso role, GradeArbitrage gradeArbitrage) {
|
||||
List<String> toRemove = new ArrayList<>(List.of("club_president", "club_tresorier", "club_secretaire",
|
||||
"asseseur", "arbitre"));
|
||||
"club_respo_intra", "asseseur", "arbitre"));
|
||||
List<String> toAdd = new ArrayList<>();
|
||||
|
||||
switch (role) {
|
||||
case PRESIDENT -> toAdd.add("club_president");
|
||||
case TRESORIER -> toAdd.add("club_tresorier");
|
||||
case SECRETAIRE -> toAdd.add("club_secretaire");
|
||||
case PRESIDENT, VPRESIDENT -> toAdd.add("club_president");
|
||||
case TRESORIER, VTRESORIER -> toAdd.add("club_tresorier");
|
||||
case SECRETAIRE, VSECRETAIRE -> toAdd.add("club_secretaire");
|
||||
case MEMBREBUREAU -> toAdd.add("club_respo_intra");
|
||||
}
|
||||
switch (gradeArbitrage) {
|
||||
case ARBITRE -> toAdd.addAll(List.of("asseseur", "arbitre"));
|
||||
@ -132,7 +143,8 @@ public class KeycloakService {
|
||||
|
||||
public Uni<List<String>> fetchRole(String id) {
|
||||
return vertx.getOrCreateContext().executeBlocking(() ->
|
||||
keycloak.realm(realm).users().get(id).roles().realmLevel().listEffective().stream().map(RoleRepresentation::getName).toList());
|
||||
keycloak.realm(realm).users().get(id).roles().realmLevel().listEffective().stream()
|
||||
.map(RoleRepresentation::getName).toList());
|
||||
}
|
||||
|
||||
public Uni<?> updateRole(String id, List<String> toAdd, List<String> toRemove) {
|
||||
@ -184,17 +196,19 @@ public class KeycloakService {
|
||||
RequiredAction.UPDATE_PASSWORD.name()));
|
||||
|
||||
try (Response response = keycloak.realm(realm).users().create(user)) {
|
||||
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo().equals(Response.Status.CONFLICT))
|
||||
if (!response.getStatusInfo().equals(Response.Status.CREATED) && !response.getStatusInfo()
|
||||
.equals(Response.Status.CONFLICT))
|
||||
throw new KeycloakException("Fail to creat user %s (reason=%s)".formatted(login,
|
||||
response.getStatusInfo().getReasonPhrase()));
|
||||
}
|
||||
|
||||
String finalLogin = login;
|
||||
return getUser(login).orElseThrow(() -> new KeycloakException("Fail to fetch user %s".formatted(finalLogin)));
|
||||
return getUser(login).orElseThrow(
|
||||
() -> new KeycloakException("Fail to fetch user %s".formatted(finalLogin)));
|
||||
})
|
||||
.invoke(user -> keycloak.realm(realm).users().get(user.getId())
|
||||
.executeActionsEmail(List.of(RequiredAction.VERIFY_EMAIL.name(),
|
||||
RequiredAction.UPDATE_PASSWORD.name())))
|
||||
//.invoke(user -> keycloak.realm(realm).users().get(user.getId()) // TODO enable for production
|
||||
// .executeActionsEmail(List.of(RequiredAction.VERIFY_EMAIL.name(),
|
||||
// RequiredAction.UPDATE_PASSWORD.name())))
|
||||
.invoke(user -> membreModel.setUserId(user.getId()))
|
||||
.call(user -> membreService.setUserId(membreModel.getId(), user.getId()))
|
||||
.call(user -> setClubGroupMembre(membreModel, membreModel.getClub()));
|
||||
@ -216,7 +230,31 @@ public class KeycloakService {
|
||||
});
|
||||
}
|
||||
|
||||
private Optional<UserRepresentation> getUser(String username) {
|
||||
public Uni<?> removeClubGroup(String clubId) {
|
||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
keycloak.realm(realm).groups().group(clubId).remove();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public Uni<?> clearUser(String userId) {
|
||||
List<String> toRemove = new ArrayList<>(
|
||||
List.of("club_president", "club_tresorier", "club_secretaire", "club_respo_intra"));
|
||||
|
||||
return vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
UserResource user = keycloak.realm(realm).users().get(userId);
|
||||
|
||||
RoleScopeResource resource = user.roles().realmLevel();
|
||||
List<RoleRepresentation> roles = keycloak.realm(realm).roles().list();
|
||||
resource.remove(roles.stream().filter(r -> toRemove.contains(r.getName())).toList());
|
||||
|
||||
user.groups().stream().filter(g -> g.getPath().startsWith("/club"))
|
||||
.forEach(g -> user.leaveGroup(g.getId()));
|
||||
return "OK";
|
||||
});
|
||||
}
|
||||
|
||||
public Optional<UserRepresentation> getUser(String username) {
|
||||
List<UserRepresentation> users = keycloak.realm(realm).users().searchByUsername(username, true);
|
||||
|
||||
if (users.isEmpty())
|
||||
@ -225,8 +263,19 @@ public class KeycloakService {
|
||||
return Optional.of(users.get(0));
|
||||
}
|
||||
|
||||
|
||||
public Optional<UserRepresentation> getUser(UUID userId) {
|
||||
UserResource user = keycloak.realm(realm).users().get(userId.toString());
|
||||
if (user == null)
|
||||
return Optional.empty();
|
||||
else
|
||||
return Optional.of(user.toRepresentation());
|
||||
}
|
||||
|
||||
private String makeLogin(MembreModel model) {
|
||||
return Normalizer.normalize((model.getFname().toLowerCase() + "." + model.getLname().toLowerCase()).replace(' ', '_'), Normalizer.Form.NFD)
|
||||
return Normalizer.normalize(
|
||||
(model.getFname().toLowerCase() + "." + model.getLname().toLowerCase()).replace(' ', '_'),
|
||||
Normalizer.Form.NFD)
|
||||
.replaceAll("\\p{M}", "");
|
||||
|
||||
}
|
||||
|
||||
@ -4,7 +4,11 @@ import fr.titionfire.ffsaf.data.model.LicenceModel;
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.LicenceRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.SequenceRepository;
|
||||
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||
import fr.titionfire.ffsaf.rest.from.LicenceForm;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import fr.titionfire.ffsaf.utils.SequenceType;
|
||||
import fr.titionfire.ffsaf.utils.Utils;
|
||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||
@ -12,8 +16,6 @@ import io.smallrye.mutiny.Uni;
|
||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
|
||||
import java.util.List;
|
||||
@ -29,34 +31,52 @@ public class LicenceService {
|
||||
@Inject
|
||||
CombRepository combRepository;
|
||||
|
||||
@Inject
|
||||
SequenceRepository sequenceRepository;
|
||||
|
||||
public Uni<List<LicenceModel>> getLicence(long id, Consumer<MembreModel> checkPerm) {
|
||||
return combRepository.findById(id).invoke(checkPerm).chain(combRepository -> Mutiny.fetch(combRepository.getLicences()));
|
||||
return combRepository.findById(id).invoke(checkPerm)
|
||||
.chain(combRepository -> Mutiny.fetch(combRepository.getLicences()));
|
||||
}
|
||||
|
||||
public Uni<List<LicenceModel>> getCurrentSaisonLicence(JsonWebToken idToken) {
|
||||
if (idToken == null)
|
||||
public Uni<List<LicenceModel>> getCurrentSaisonLicence(SecurityCtx securityCtx) {
|
||||
if (securityCtx.getSubject() == null)
|
||||
return repository.find("saison = ?1", Utils.getSaison()).list();
|
||||
|
||||
return combRepository.find("userId = ?1", idToken.getSubject()).firstResult().map(MembreModel::getClub)
|
||||
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult().map(MembreModel::getClub)
|
||||
.chain(clubModel -> combRepository.find("club = ?1", clubModel).list())
|
||||
.chain(membres -> repository.find("saison = ?1 AND membre IN ?2", Utils.getSaison(), membres).list());
|
||||
}
|
||||
|
||||
public Uni<LicenceModel> setLicence(long id, LicenceForm form) {
|
||||
if (form.getId() == -1) {
|
||||
return combRepository.findById(id).chain(combRepository -> {
|
||||
return combRepository.findById(id).chain(membreModel -> {
|
||||
LicenceModel model = new LicenceModel();
|
||||
model.setMembre(combRepository);
|
||||
model.setMembre(membreModel);
|
||||
model.setSaison(form.getSaison());
|
||||
model.setCertificate(form.isCertificate());
|
||||
model.setCertificate(form.getCertificate());
|
||||
model.setValidate(form.isValidate());
|
||||
return Panache.withTransaction(() -> repository.persist(model));
|
||||
return Panache.withTransaction(() -> repository.persist(model)
|
||||
.call(m -> (m.isValidate() && membreModel.getLicence() <= 0) ?
|
||||
sequenceRepository.getNextValueInTransaction(SequenceType.Licence)
|
||||
.invoke(i -> membreModel.setLicence(Math.toIntExact(i)))
|
||||
.chain(() -> combRepository.persist(membreModel))
|
||||
: Uni.createFrom().nullItem()
|
||||
));
|
||||
});
|
||||
} else {
|
||||
return repository.findById(form.getId()).chain(model -> {
|
||||
model.setCertificate(form.isCertificate());
|
||||
model.setCertificate(form.getCertificate());
|
||||
model.setValidate(form.isValidate());
|
||||
return Panache.withTransaction(() -> repository.persist(model));
|
||||
return Panache.withTransaction(() -> repository.persist(model)
|
||||
.call(m -> m.isValidate() ? Mutiny.fetch(m.getMembre())
|
||||
.call(membreModel -> (membreModel.getLicence() <= 0) ?
|
||||
sequenceRepository.getNextValueInTransaction(SequenceType.Licence)
|
||||
.invoke(i -> membreModel.setLicence(Math.toIntExact(i)))
|
||||
.chain(() -> combRepository.persist(membreModel))
|
||||
: Uni.createFrom().nullItem())
|
||||
: Uni.createFrom().nullItem()
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -68,20 +88,21 @@ public class LicenceService {
|
||||
public Uni<LicenceModel> askLicence(long id, LicenceForm form, Consumer<MembreModel> checkPerm) {
|
||||
return combRepository.findById(id).invoke(checkPerm).chain(membreModel -> {
|
||||
if (form.getId() == -1) {
|
||||
return repository.find("saison = ?1 AND membre = ?2", Utils.getSaison(), membreModel).count().invoke(Unchecked.consumer(count -> {
|
||||
if (count > 0)
|
||||
throw new BadRequestException();
|
||||
})).chain(__ -> combRepository.findById(id).chain(combRepository -> {
|
||||
LicenceModel model = new LicenceModel();
|
||||
model.setMembre(combRepository);
|
||||
model.setSaison(Utils.getSaison());
|
||||
model.setCertificate(form.isCertificate());
|
||||
model.setValidate(false);
|
||||
return Panache.withTransaction(() -> repository.persist(model));
|
||||
}));
|
||||
return repository.find("saison = ?1 AND membre = ?2", Utils.getSaison(), membreModel).count()
|
||||
.invoke(Unchecked.consumer(count -> {
|
||||
if (count > 0)
|
||||
throw new DBadRequestException("Licence déjà demandée");
|
||||
})).chain(__ -> combRepository.findById(id).chain(combRepository -> {
|
||||
LicenceModel model = new LicenceModel();
|
||||
model.setMembre(combRepository);
|
||||
model.setSaison(Utils.getSaison());
|
||||
model.setCertificate(form.getCertificate());
|
||||
model.setValidate(false);
|
||||
return Panache.withTransaction(() -> repository.persist(model));
|
||||
}));
|
||||
} else {
|
||||
return repository.findById(form.getId()).chain(model -> {
|
||||
model.setCertificate(form.isCertificate());
|
||||
model.setCertificate(form.getCertificate());
|
||||
return Panache.withTransaction(() -> repository.persist(model));
|
||||
});
|
||||
}
|
||||
|
||||
@ -0,0 +1,114 @@
|
||||
package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.MatchRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.PouleRepository;
|
||||
import fr.titionfire.ffsaf.rest.data.MatchData;
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@WithSession
|
||||
@ApplicationScoped
|
||||
public class MatchService {
|
||||
|
||||
@Inject
|
||||
MatchRepository repository;
|
||||
|
||||
@Inject
|
||||
PouleRepository pouleRepository;
|
||||
|
||||
@Inject
|
||||
CombRepository combRepository;
|
||||
|
||||
@Inject
|
||||
CompetPermService permService;
|
||||
|
||||
public Uni<MatchData> getById(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
|
||||
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
|
||||
.onItem().ifNull().failWith(() -> new RuntimeException("Match not found"))
|
||||
.call(data -> permService.hasViewPerm(securityCtx, data.getPoule().getCompet()))
|
||||
.map(MatchData::fromModel);
|
||||
}
|
||||
|
||||
public Uni<List<MatchData>> getAllByPoule(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
|
||||
return pouleRepository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
|
||||
.onItem().ifNull().failWith(() -> new RuntimeException("Poule not found"))
|
||||
.call(data -> permService.hasViewPerm(securityCtx, data.getCompet()))
|
||||
.chain(data -> repository.list("poule = ?1", data.getId())
|
||||
.map(o -> o.stream().map(MatchData::fromModel).toList()));
|
||||
}
|
||||
|
||||
public Uni<MatchData> addOrUpdate(SecurityCtx securityCtx, CompetitionSystem system, MatchData data) {
|
||||
return repository.find("systemId = ?1 AND system = ?2", data.getId(), system).firstResult()
|
||||
.chain(o -> {
|
||||
if (o == null) {
|
||||
return pouleRepository.find("systemId = ?1 AND system = ?2", data.getPoule(), system)
|
||||
.firstResult()
|
||||
.onItem().ifNull().failWith(() -> new RuntimeException("Poule not found"))
|
||||
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
|
||||
.map(pouleModel -> {
|
||||
MatchModel model = new MatchModel();
|
||||
|
||||
model.setId(null);
|
||||
model.setSystem(system);
|
||||
model.setSystemId(data.getId());
|
||||
model.setPoule(pouleModel);
|
||||
return model;
|
||||
});
|
||||
} else {
|
||||
return pouleRepository.find("systemId = ?1 AND system = ?2", data.getPoule(), system)
|
||||
.firstResult()
|
||||
.onItem().ifNull().failWith(() -> new RuntimeException("Poule not found"))
|
||||
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
|
||||
.map(__ -> o);
|
||||
}
|
||||
}
|
||||
)
|
||||
.chain(o -> {
|
||||
o.setC1_str(data.getC1_str());
|
||||
o.setC2_str(data.getC2_str());
|
||||
o.setPoule_ord(data.getPoule_ord());
|
||||
o.getScores().clear();
|
||||
o.getScores().addAll(data.getScores());
|
||||
|
||||
return Uni.createFrom().nullItem()
|
||||
.chain(() -> (data.getC1_id() == null) ?
|
||||
Uni.createFrom().nullItem() : combRepository.findById(data.getC1_id()))
|
||||
.invoke(o::setC1_id)
|
||||
.chain(() -> (data.getC1_id() == null) ?
|
||||
Uni.createFrom().nullItem() : combRepository.findById(data.getC2_id()))
|
||||
.invoke(o::setC2_id)
|
||||
.chain(() -> Panache.withTransaction(() -> repository.persist(o)));
|
||||
})
|
||||
.map(MatchData::fromModel);
|
||||
}
|
||||
|
||||
public Uni<?> updateScore(SecurityCtx securityCtx, CompetitionSystem system, Long id,
|
||||
List<ScoreEmbeddable> scores) {
|
||||
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
|
||||
.onItem().ifNull().failWith(() -> new RuntimeException("Match not found"))
|
||||
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getPoule().getCompet()))
|
||||
.invoke(data -> {
|
||||
data.getScores().clear();
|
||||
data.getScores().addAll(scores);
|
||||
})
|
||||
.chain(data -> Panache.withTransaction(() -> repository.persist(data)))
|
||||
.map(o -> "OK");
|
||||
}
|
||||
|
||||
public Uni<?> delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
|
||||
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
|
||||
.onItem().ifNull().failWith(() -> new RuntimeException("Match not found"))
|
||||
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getPoule().getCompet()))
|
||||
.chain(data -> Panache.withTransaction(() -> repository.delete(data)));
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,12 @@
|
||||
package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
import com.lowagie.text.*;
|
||||
import com.lowagie.text.pdf.BaseFont;
|
||||
import com.lowagie.text.pdf.PdfPCell;
|
||||
import com.lowagie.text.pdf.PdfPTable;
|
||||
import com.lowagie.text.pdf.PdfWriter;
|
||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||
import fr.titionfire.ffsaf.data.model.LicenceModel;
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.data.repository.ClubRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.CombRepository;
|
||||
@ -8,7 +14,12 @@ import fr.titionfire.ffsaf.data.repository.LicenceRepository;
|
||||
import fr.titionfire.ffsaf.net2.ServerCustom;
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleCombModel;
|
||||
import fr.titionfire.ffsaf.net2.request.SReqComb;
|
||||
import fr.titionfire.ffsaf.rest.data.MeData;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleLicence;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||
import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
|
||||
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
||||
import fr.titionfire.ffsaf.utils.*;
|
||||
@ -17,15 +28,22 @@ import io.quarkus.hibernate.reactive.panache.PanacheQuery;
|
||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
import io.quarkus.vertx.VertxContextSupport;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.ForbiddenException;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
@WithSession
|
||||
@ -44,6 +62,9 @@ public class MembreService {
|
||||
@Inject
|
||||
KeycloakService keycloakService;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
public SimpleCombModel find(int licence, String np) throws Throwable {
|
||||
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() ->
|
||||
repository.find("licence = ?1 AND (lname ILIKE ?2 OR fname ILIKE ?2)",
|
||||
@ -51,7 +72,8 @@ public class MembreService {
|
||||
}
|
||||
|
||||
public SimpleCombModel findByIdOptionalComb(long id) throws Throwable {
|
||||
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleCombModel::fromModel)));
|
||||
return VertxContextSupport.subscribeAndAwait(
|
||||
() -> Panache.withTransaction(() -> repository.findById(id).map(SimpleCombModel::fromModel)));
|
||||
}
|
||||
|
||||
public Uni<PageResult<SimpleMembre>> searchAdmin(int limit, int page, String search, String club) {
|
||||
@ -78,7 +100,8 @@ public class MembreService {
|
||||
return repository.find("userId = ?1", subject).firstResult()
|
||||
.chain(membreModel -> {
|
||||
PanacheQuery<MembreModel> query = repository.find("club = ?1 AND (lname LIKE ?2 OR fname LIKE ?2)",
|
||||
Sort.ascending("fname", "lname"), membreModel.getClub(), finalSearch).page(Page.ofSize(limit));
|
||||
Sort.ascending("fname", "lname"), membreModel.getClub(), finalSearch)
|
||||
.page(Page.ofSize(limit));
|
||||
return getPageResult(query, limit, page);
|
||||
});
|
||||
}
|
||||
@ -90,7 +113,7 @@ public class MembreService {
|
||||
.call(result -> query.count().invoke(result::setResult_count))
|
||||
.call(result -> query.pageCount()
|
||||
.invoke(Unchecked.consumer(pages -> {
|
||||
if (page > pages) throw new BadRequestException();
|
||||
if (page > pages) throw new DBadRequestException("Page out of range");
|
||||
}))
|
||||
.invoke(result::setPage_count))
|
||||
.call(result -> query.page(Page.of(page, limit)).list()
|
||||
@ -102,13 +125,23 @@ public class MembreService {
|
||||
return repository.findById(id);
|
||||
}
|
||||
|
||||
public Uni<MembreModel> getByIdWithLicence(long id) {
|
||||
return repository.findById(id)
|
||||
.call(m -> Mutiny.fetch(m.getLicences()));
|
||||
}
|
||||
|
||||
public Uni<MembreModel> getByLicence(long licence) {
|
||||
return repository.find("licence = ?1", licence).firstResult();
|
||||
}
|
||||
|
||||
public Uni<String> update(long id, FullMemberForm membre) {
|
||||
return repository.findById(id)
|
||||
.chain(membreModel -> clubRepository.findById(membre.getClub()).map(club -> new Pair<>(membreModel, club)))
|
||||
.chain(membreModel -> clubRepository.findById(membre.getClub())
|
||||
.map(club -> new Pair<>(membreModel, club)))
|
||||
.onItem().transformToUni(pair -> {
|
||||
MembreModel m = pair.getKey();
|
||||
m.setFname(membre.getFname());
|
||||
m.setLname(membre.getLname());
|
||||
m.setLname(membre.getLname().toUpperCase());
|
||||
m.setClub(pair.getValue());
|
||||
m.setCountry(membre.getCountry());
|
||||
m.setBirth_date(membre.getBirth_date());
|
||||
@ -119,49 +152,56 @@ public class MembreService {
|
||||
m.setEmail(membre.getEmail());
|
||||
return Panache.withTransaction(() -> repository.persist(m));
|
||||
})
|
||||
.invoke(membreModel -> SReqComb.sendIfNeed(serverCustom.clients, SimpleCombModel.fromModel(membreModel)))
|
||||
.invoke(membreModel -> SReqComb.sendIfNeed(serverCustom.clients,
|
||||
SimpleCombModel.fromModel(membreModel)))
|
||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||
keycloakService.setClubGroupMembre(membreModel, membreModel.getClub()) : Uni.createFrom().nullItem())
|
||||
((membreModel.getClub() != null) ?
|
||||
keycloakService.setClubGroupMembre(membreModel, membreModel.getClub()) :
|
||||
keycloakService.clearUser(membreModel.getUserId()))
|
||||
: Uni.createFrom().nullItem())
|
||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||
keycloakService.setAutoRoleMembre(membreModel.getUserId(), membreModel.getRole(),
|
||||
membreModel.getGrade_arbitrage()) : Uni.createFrom().nullItem())
|
||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||
keycloakService.setEmail(membreModel.getUserId(), membreModel.getEmail()) : Uni.createFrom().nullItem())
|
||||
keycloakService.setEmail(membreModel.getUserId(), membreModel.getEmail()) : Uni.createFrom()
|
||||
.nullItem())
|
||||
.map(__ -> "OK");
|
||||
}
|
||||
|
||||
public Uni<String> update(long id, ClubMemberForm membre, JsonWebToken idToken, SecurityIdentity securityIdentity) {
|
||||
public Uni<String> update(long id, ClubMemberForm membre, SecurityCtx securityCtx) {
|
||||
return repository.findById(id)
|
||||
.invoke(Unchecked.consumer(membreModel -> {
|
||||
if (!GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
|
||||
throw new ForbiddenException();
|
||||
if (!securityCtx.isInClubGroup(membreModel.getClub().getId()))
|
||||
throw new DForbiddenException();
|
||||
}))
|
||||
.invoke(Unchecked.consumer(membreModel -> {
|
||||
RoleAsso source = RoleAsso.MEMBRE;
|
||||
if (securityIdentity.getRoles().contains("club_president")) source = RoleAsso.PRESIDENT;
|
||||
else if (securityIdentity.getRoles().contains("club_secretaire")) source = RoleAsso.SECRETAIRE;
|
||||
else if (securityIdentity.getRoles().contains("club_respo_intra")) source = RoleAsso.SECRETAIRE;
|
||||
if (!membre.getRole().equals(membreModel.getRole()) && membre.getRole().level > source.level)
|
||||
throw new ForbiddenException();
|
||||
if (securityCtx.roleHas("club_president")) source = RoleAsso.PRESIDENT;
|
||||
else if (securityCtx.roleHas("club_secretaire")) source = RoleAsso.SECRETAIRE;
|
||||
else if (securityCtx.roleHas("club_respo_intra")) source = RoleAsso.MEMBREBUREAU;
|
||||
if (!membre.getRole().equals(membreModel.getRole()) && membre.getRole().level >= source.level)
|
||||
throw new DForbiddenException("Permission insuffisante");
|
||||
}))
|
||||
.onItem().transformToUni(target -> {
|
||||
target.setFname(membre.getFname());
|
||||
target.setLname(membre.getLname());
|
||||
target.setLname(membre.getLname().toUpperCase());
|
||||
target.setCountry(membre.getCountry());
|
||||
target.setBirth_date(membre.getBirth_date());
|
||||
target.setGenre(membre.getGenre());
|
||||
target.setCategorie(membre.getCategorie());
|
||||
target.setEmail(membre.getEmail());
|
||||
if (!idToken.getSubject().equals(target.getUserId()))
|
||||
if (!securityCtx.getSubject().equals(target.getUserId()))
|
||||
target.setRole(membre.getRole());
|
||||
return Panache.withTransaction(() -> repository.persist(target));
|
||||
})
|
||||
.invoke(membreModel -> SReqComb.sendIfNeed(serverCustom.clients, SimpleCombModel.fromModel(membreModel)))
|
||||
.invoke(membreModel -> SReqComb.sendIfNeed(serverCustom.clients,
|
||||
SimpleCombModel.fromModel(membreModel)))
|
||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||
keycloakService.setAutoRoleMembre(membreModel.getUserId(), membreModel.getRole(),
|
||||
membreModel.getGrade_arbitrage()) : Uni.createFrom().nullItem())
|
||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||
keycloakService.setEmail(membreModel.getUserId(), membreModel.getEmail()) : Uni.createFrom().nullItem())
|
||||
keycloakService.setEmail(membreModel.getUserId(), membreModel.getEmail()) : Uni.createFrom()
|
||||
.nullItem())
|
||||
.map(__ -> "OK");
|
||||
}
|
||||
|
||||
@ -171,7 +211,8 @@ public class MembreService {
|
||||
MembreModel model = getMembreModel(input, clubModel);
|
||||
return Panache.withTransaction(() -> repository.persist(model));
|
||||
})
|
||||
.invoke(membreModel -> SReqComb.sendIfNeedAdd(serverCustom.clients, SimpleCombModel.fromModel(membreModel)))
|
||||
.invoke(membreModel -> SReqComb.sendIfNeedAdd(serverCustom.clients,
|
||||
SimpleCombModel.fromModel(membreModel)))
|
||||
.map(MembreModel::getId);
|
||||
}
|
||||
|
||||
@ -183,7 +224,8 @@ public class MembreService {
|
||||
model.setGrade_arbitrage(GradeArbitrage.NA);
|
||||
return Panache.withTransaction(() -> repository.persist(model));
|
||||
})
|
||||
.invoke(membreModel -> SReqComb.sendIfNeedAdd(serverCustom.clients, SimpleCombModel.fromModel(membreModel)))
|
||||
.invoke(membreModel -> SReqComb.sendIfNeedAdd(serverCustom.clients,
|
||||
SimpleCombModel.fromModel(membreModel)))
|
||||
.map(MembreModel::getId);
|
||||
}
|
||||
|
||||
@ -196,21 +238,22 @@ public class MembreService {
|
||||
.map(__ -> "Ok");
|
||||
}
|
||||
|
||||
public Uni<String> delete(long id, JsonWebToken idToken) {
|
||||
public Uni<String> delete(long id, SecurityCtx securityCtx) {
|
||||
return repository.findById(id)
|
||||
.invoke(Unchecked.consumer(membreModel -> {
|
||||
if (!GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
|
||||
throw new ForbiddenException();
|
||||
if (!securityCtx.isInClubGroup(membreModel.getClub().getId()))
|
||||
throw new DForbiddenException();
|
||||
}))
|
||||
.call(membreModel -> licenceRepository.find("membre = ?1", membreModel).count()
|
||||
.invoke(Unchecked.consumer(l -> {
|
||||
if (l > 0)
|
||||
throw new BadRequestException();
|
||||
throw new DBadRequestException("Impossible de supprimer un membre avec des licences");
|
||||
})))
|
||||
.call(membreModel -> (membreModel.getUserId() != null) ?
|
||||
keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem())
|
||||
.call(membreModel -> Panache.withTransaction(() -> repository.delete(membreModel)))
|
||||
.invoke(membreModel -> SReqComb.sendRm(serverCustom.clients, id))
|
||||
.call(__ -> Utils.deleteMedia(id, media, "ppMembre"))
|
||||
.map(__ -> "Ok");
|
||||
}
|
||||
|
||||
@ -235,4 +278,221 @@ public class MembreService {
|
||||
model.setGrade_arbitrage(input.getGrade_arbitrage());
|
||||
return model;
|
||||
}
|
||||
|
||||
public Uni<List<SimpleMembre>> getSimilar(String fname, String lname) {
|
||||
return repository.listAll().map(membreModels -> membreModels.stream()
|
||||
.filter(m -> StringSimilarity.similarity(m.getFname(), fname) <= 3 &&
|
||||
StringSimilarity.similarity(m.getLname(), lname) <= 3)
|
||||
.map(SimpleMembre::fromModel).toList());
|
||||
}
|
||||
|
||||
public Uni<MeData> getMembre(String subject) {
|
||||
MeData meData = new MeData();
|
||||
return repository.find("userId = ?1", subject).firstResult()
|
||||
.invoke(meData::setMembre)
|
||||
.chain(membreModel -> Mutiny.fetch(membreModel.getLicences()))
|
||||
.map(licences -> licences.stream().map(SimpleLicence::fromModel).toList())
|
||||
.invoke(meData::setLicences)
|
||||
.map(__ -> meData);
|
||||
}
|
||||
|
||||
public Uni<Response> getLicencePdf(String subject) {
|
||||
return getLicencePdf(repository.find("userId = ?1", subject).firstResult()
|
||||
.call(m -> Mutiny.fetch(m.getLicences())));
|
||||
}
|
||||
|
||||
public Uni<Response> getLicencePdf(Uni<MembreModel> uniBase) {
|
||||
return uniBase
|
||||
.map(Unchecked.function(m -> {
|
||||
LicenceModel licence = m.getLicences().stream()
|
||||
.filter(licenceModel -> licenceModel.getSaison() == Utils.getSaison() && licenceModel.isValidate())
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new DNotFoundException("Pas de licence pour la saison en cours"));
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
make_pdf(m, out, licence);
|
||||
|
||||
byte[] buff = out.toByteArray();
|
||||
String mimeType = "application/pdf";
|
||||
|
||||
Response.ResponseBuilder resp = Response.ok(buff);
|
||||
resp.type(MediaType.APPLICATION_OCTET_STREAM);
|
||||
resp.header(HttpHeaders.CONTENT_LENGTH, buff.length);
|
||||
resp.header(HttpHeaders.CONTENT_TYPE, mimeType);
|
||||
resp.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"inline; " + "filename=\"Attestation d'adhésion " + Utils.getSaison() + "-" +
|
||||
(Utils.getSaison() + 1) + " de " + m.getLname() + " " + m.getFname() + ".pdf\"");
|
||||
return resp.build();
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void make_pdf(MembreModel m, ByteArrayOutputStream out, LicenceModel licence) throws IOException {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
|
||||
Document document = new Document();
|
||||
PdfWriter.getInstance(document, out);
|
||||
document.open();
|
||||
|
||||
document.addCreator("FFSAF");
|
||||
document.addTitle(
|
||||
"Attestation d'adhésion " + Utils.getSaison() + "-" + (Utils.getSaison() + 1) + " de " + m.getLname() + " " + m.getFname());
|
||||
document.addCreationDate();
|
||||
document.addProducer("https://www.ffsaf.fr");
|
||||
|
||||
InputStream fontStream = MembreService.class.getClassLoader().getResourceAsStream("asset/DMSans-Regular.ttf");
|
||||
if (fontStream == null) {
|
||||
throw new IOException("Font file not found");
|
||||
}
|
||||
BaseFont customFont = BaseFont.createFont("asset/DMSans-Regular.ttf", BaseFont.WINANSI, BaseFont.EMBEDDED, true,
|
||||
null, fontStream.readAllBytes());
|
||||
|
||||
// Adding font
|
||||
Font headerFont = new Font(customFont, 26, Font.BOLD);
|
||||
Font subHeaderFont = new Font(customFont, 16, Font.BOLD);
|
||||
Font bigFont = new Font(customFont, 18, Font.BOLD);
|
||||
Font bodyFont = new Font(customFont, 15, Font.BOLD);
|
||||
Font smallFont = new Font(customFont, 10, Font.NORMAL);
|
||||
|
||||
// Creating the main table
|
||||
PdfPTable mainTable = new PdfPTable(2);
|
||||
mainTable.setWidthPercentage(100);
|
||||
mainTable.setSpacingBefore(20f);
|
||||
mainTable.setSpacingAfter(0f);
|
||||
mainTable.setWidths(new float[]{120, 300});
|
||||
mainTable.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
|
||||
|
||||
// Adding logo
|
||||
Image logo = Image.getInstance(
|
||||
Objects.requireNonNull(
|
||||
getClass().getClassLoader().getResource("asset/FFSSAF-bord-blanc-fond-transparent.png")));
|
||||
logo.scaleToFit(120, 120);
|
||||
PdfPCell logoCell = new PdfPCell(logo);
|
||||
logoCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||
logoCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
logoCell.setPadding(0);
|
||||
logoCell.setRowspan(1);
|
||||
logoCell.setBorder(PdfPCell.NO_BORDER);
|
||||
mainTable.addCell(logoCell);
|
||||
|
||||
// Adding header
|
||||
PdfPCell headerCell = new PdfPCell(new Phrase("FEDERATION FRANCE\nSOFT ARMORED FIGHTING", headerFont));
|
||||
headerCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||
headerCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
headerCell.setBorder(PdfPCell.NO_BORDER);
|
||||
mainTable.addCell(headerCell);
|
||||
|
||||
document.add(mainTable);
|
||||
|
||||
Paragraph addr = new Paragraph("5 place de la Barreyre\n63320 Champeix", subHeaderFont);
|
||||
addr.setAlignment(Element.ALIGN_CENTER);
|
||||
addr.setSpacingAfter(2f);
|
||||
document.add(addr);
|
||||
|
||||
Paragraph association = new Paragraph("Association loi 1901 W633001595\nSIRET 829 458 355 00015", smallFont);
|
||||
association.setAlignment(Element.ALIGN_CENTER);
|
||||
document.add(association);
|
||||
|
||||
// Adding spacing
|
||||
document.add(new Paragraph("\n\n"));
|
||||
|
||||
// Adding attestation
|
||||
PdfPTable attestationTable = new PdfPTable(1);
|
||||
attestationTable.setWidthPercentage(60);
|
||||
PdfPCell attestationCell = new PdfPCell(
|
||||
new Phrase("ATTESTATION D'ADHESION\nSaison " + Utils.getSaison() + "-" + (Utils.getSaison() + 1),
|
||||
bigFont));
|
||||
attestationCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||
attestationCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
attestationCell.setPadding(20f);
|
||||
attestationTable.addCell(attestationCell);
|
||||
document.add(attestationTable);
|
||||
|
||||
// Adding spacing
|
||||
document.add(new Paragraph("\n\n"));
|
||||
|
||||
// Adding member details table
|
||||
PdfPTable memberTable = new PdfPTable(2);
|
||||
memberTable.setWidthPercentage(100);
|
||||
memberTable.setWidths(new float[]{130, 300});
|
||||
memberTable.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
|
||||
|
||||
// Adding member photo
|
||||
Image memberPhoto;
|
||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(m.getId() + ".");
|
||||
File[] files = new File(media, "ppMembre").listFiles(filter);
|
||||
if (files != null && files.length > 0) {
|
||||
File file = files[0];
|
||||
memberPhoto = Image.getInstance(Files.readAllBytes(file.toPath()));
|
||||
} else {
|
||||
memberPhoto = Image.getInstance(
|
||||
Objects.requireNonNull(getClass().getClassLoader().getResource("asset/blank-profile-picture.png")));
|
||||
}
|
||||
memberPhoto.scaleToFit(120, 150);
|
||||
PdfPCell photoCell = new PdfPCell(memberPhoto);
|
||||
photoCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||
photoCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
photoCell.setRowspan(5);
|
||||
photoCell.setBorder(PdfPCell.NO_BORDER);
|
||||
memberTable.addCell(photoCell);
|
||||
|
||||
// Adding member details
|
||||
memberTable.addCell(new Phrase("NOM : " + m.getLname().toUpperCase(), bodyFont));
|
||||
memberTable.addCell(new Phrase("Prénom : " + m.getFname(), bodyFont));
|
||||
memberTable.addCell(new Phrase("Licence n° : " + m.getLicence(), bodyFont));
|
||||
memberTable.addCell(new Phrase("Certificat médical par Dr " + licence.getCertificate(), bodyFont));
|
||||
memberTable.addCell(new Phrase("")); // Empty cell for spacing
|
||||
|
||||
document.add(memberTable);
|
||||
|
||||
// Adding spacing
|
||||
document.add(new Paragraph("\n"));
|
||||
|
||||
Paragraph memberClub = new Paragraph("CLUB : " + m.getClub().getName().toUpperCase(), bodyFont);
|
||||
document.add(memberClub);
|
||||
|
||||
Paragraph memberClubNumber = new Paragraph("N° club : " + m.getClub().getNo_affiliation(), bodyFont);
|
||||
document.add(memberClubNumber);
|
||||
|
||||
// Adding spacing
|
||||
document.add(new Paragraph("\n"));
|
||||
|
||||
Paragraph memberBirthdate = new Paragraph(
|
||||
"Date de naissance : " + ((m.getBirth_date() == null) ? "--" : sdf.format(m.getBirth_date())),
|
||||
bodyFont);
|
||||
document.add(memberBirthdate);
|
||||
|
||||
Paragraph memberGender = new Paragraph("Sexe : " + m.getGenre().str, bodyFont);
|
||||
document.add(memberGender);
|
||||
|
||||
Paragraph memberAgeCategory = new Paragraph("Catégorie d'âge : " + m.getCategorie().getName(), bodyFont);
|
||||
document.add(memberAgeCategory);
|
||||
|
||||
// Adding spacing
|
||||
document.add(new Paragraph("\n\n"));
|
||||
|
||||
// Adding attestation text
|
||||
PdfPTable textTable = new PdfPTable(1);
|
||||
textTable.setWidthPercentage(100);
|
||||
PdfPCell textCell = new PdfPCell(new Phrase(
|
||||
"""
|
||||
Ce document atteste que l’adhérent
|
||||
- est valablement enregistré auprès de la FFSAF,
|
||||
- est assuré dans sa pratique du Béhourd Léger et du Battle Arc en entraînement et en compétition.
|
||||
|
||||
Il peut donc s’inscrire à tout tournoi organisé sous l’égide de la FFSAF s’il remplit les éventuelles
|
||||
conditions de qualification.
|
||||
Il peut participer à tout entraînement dans un club affilié si celui ci autorise les visiteurs.""",
|
||||
smallFont));
|
||||
textCell.setHorizontalAlignment(Element.ALIGN_LEFT);
|
||||
textCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||
textCell.setBorder(PdfPCell.NO_BORDER);
|
||||
textTable.addCell(textCell);
|
||||
document.add(textTable);
|
||||
|
||||
// Close the document
|
||||
document.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,284 @@
|
||||
package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.data.model.PouleModel;
|
||||
import fr.titionfire.ffsaf.data.model.TreeModel;
|
||||
import fr.titionfire.ffsaf.data.repository.*;
|
||||
import fr.titionfire.ffsaf.rest.data.PouleData;
|
||||
import fr.titionfire.ffsaf.rest.data.PouleFullData;
|
||||
import fr.titionfire.ffsaf.rest.data.TreeData;
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import io.quarkus.hibernate.reactive.panache.Panache;
|
||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@WithSession
|
||||
@ApplicationScoped
|
||||
public class PouleService {
|
||||
|
||||
@Inject
|
||||
PouleRepository repository;
|
||||
|
||||
@Inject
|
||||
CompetitionRepository competRepository;
|
||||
|
||||
@Inject
|
||||
MatchRepository matchRepository;
|
||||
|
||||
@Inject
|
||||
TreeRepository treeRepository;
|
||||
|
||||
@Inject
|
||||
CombRepository combRepository;
|
||||
|
||||
@Inject
|
||||
CompetPermService permService;
|
||||
|
||||
public Uni<PouleData> getById(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
|
||||
return repository.find("systemId = ?1 AND system = ?2", id, system)
|
||||
.firstResult()
|
||||
.onItem().ifNull().failWith(() -> new RuntimeException("Poule not found"))
|
||||
.call(data -> permService.hasViewPerm(securityCtx, data.getCompet()))
|
||||
.map(PouleData::fromModel);
|
||||
}
|
||||
|
||||
public Uni<List<PouleData>> getAll(SecurityCtx securityCtx, CompetitionSystem system) {
|
||||
return repository.list("system = ?1", system)
|
||||
.chain(o ->
|
||||
permService.getAllHaveAccess(securityCtx.getSubject())
|
||||
.chain(map -> Uni.createFrom().item(o.stream()
|
||||
.filter(p -> {
|
||||
if (securityCtx.getSubject().equals(p.getCompet().getOwner()))
|
||||
return true;
|
||||
if (p.getSystem() == CompetitionSystem.SAFCA) {
|
||||
if (map.containsKey(p.getCompet().getId()))
|
||||
return map.get(p.getId()).equals("admin");
|
||||
return securityCtx.roleHas("federation_admin")
|
||||
|| securityCtx.roleHas("safca_super_admin");
|
||||
}
|
||||
return securityCtx.roleHas("federation_admin");
|
||||
})
|
||||
.map(PouleData::fromModel).toList())
|
||||
));
|
||||
}
|
||||
|
||||
public Uni<PouleData> addOrUpdate(SecurityCtx securityCtx, CompetitionSystem system, PouleData data) {
|
||||
return repository.find("systemId = ?1 AND system = ?2", data.getId(), system).firstResult()
|
||||
.chain(o -> {
|
||||
if (o == null) {
|
||||
return competRepository.findById(data.getCompet())
|
||||
.onItem().ifNull().failWith(() -> new RuntimeException("Competition not found"))
|
||||
.call(o2 -> permService.hasEditPerm(securityCtx, o2))
|
||||
.chain(competitionModel -> {
|
||||
PouleModel model = new PouleModel();
|
||||
|
||||
model.setId(null);
|
||||
model.setSystem(system);
|
||||
model.setSystemId(data.getId());
|
||||
model.setCompet(competitionModel);
|
||||
model.setName(data.getName());
|
||||
model.setMatchs(new ArrayList<>());
|
||||
model.setTree(new ArrayList<>());
|
||||
model.setType(data.getType());
|
||||
|
||||
return Panache.withTransaction(() -> repository.persist(model));
|
||||
});
|
||||
} else {
|
||||
o.setName(data.getName());
|
||||
o.setType(data.getType());
|
||||
return Panache.withTransaction(() -> repository.persist(o));
|
||||
}
|
||||
}).map(PouleData::fromModel);
|
||||
}
|
||||
|
||||
private MatchModel findMatch(List<MatchModel> matchModelList, Long id) {
|
||||
return matchModelList.stream().filter(m -> m.getSystemId().equals(id))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private TreeModel findNode(List<TreeModel> node, Long match_id) {
|
||||
return node.stream().filter(m -> m.getMatch().getSystemId().equals(match_id))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private void flatTreeChild(TreeModel current, ArrayList<TreeModel> node) {
|
||||
if (current != null) {
|
||||
node.add(current);
|
||||
flatTreeChild(current.getLeft(), node);
|
||||
flatTreeChild(current.getRight(), node);
|
||||
}
|
||||
}
|
||||
|
||||
private void flatTreeChild(TreeData current, ArrayList<TreeData> node) {
|
||||
if (current != null) {
|
||||
node.add(current);
|
||||
flatTreeChild(current.getLeft(), node);
|
||||
flatTreeChild(current.getRight(), node);
|
||||
}
|
||||
}
|
||||
|
||||
private Uni<TreeModel> persisteTree(TreeData data, List<TreeModel> node, PouleModel poule,
|
||||
List<MatchModel> matchModelList) {
|
||||
TreeModel mm = findNode(node, data.getMatch());
|
||||
if (mm == null) {
|
||||
mm = new TreeModel();
|
||||
mm.setId(null);
|
||||
}
|
||||
mm.setLevel(data.getLevel());
|
||||
mm.setPoule(poule.getId());
|
||||
mm.setMatch(findMatch(matchModelList, data.getMatch()));
|
||||
|
||||
return Uni.createFrom().item(mm)
|
||||
.call(o -> (data.getLeft() == null ? Uni.createFrom().nullItem().invoke(o1 -> o.setLeft(null)) :
|
||||
persisteTree(data.getLeft(), node, poule, matchModelList).invoke(o::setLeft)))
|
||||
.call(o -> (data.getRight() == null ? Uni.createFrom().nullItem().invoke(o1 -> o.setRight(null)) :
|
||||
persisteTree(data.getRight(), node, poule, matchModelList).invoke(o::setRight)))
|
||||
.chain(o -> Panache.withTransaction(() -> treeRepository.persist(o)));
|
||||
}
|
||||
|
||||
public Uni<?> syncPoule(SecurityCtx securityCtx, CompetitionSystem system, PouleFullData data) {
|
||||
return repository.find("systemId = ?1 AND system = ?2", data.getId(), system)
|
||||
.firstResult()
|
||||
.onItem().ifNotNull().call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
|
||||
.onItem().ifNull().switchTo(
|
||||
() -> competRepository.findById(data.getCompet())
|
||||
.onItem().ifNull().failWith(() -> new RuntimeException("Compet not found"))
|
||||
.call(o -> permService.hasEditPerm(securityCtx, o))
|
||||
.map(o -> {
|
||||
PouleModel model = new PouleModel();
|
||||
model.setId(null);
|
||||
model.setSystem(system);
|
||||
model.setSystemId(data.getId());
|
||||
model.setMatchs(new ArrayList<>());
|
||||
model.setTree(new ArrayList<>());
|
||||
model.setCompet(o);
|
||||
return model;
|
||||
}))
|
||||
.call(o -> Mutiny.fetch(o.getMatchs()))
|
||||
.call(o -> Mutiny.fetch(o.getTree()))
|
||||
.map(o -> {
|
||||
o.setName(data.getName());
|
||||
o.setType(data.getType());
|
||||
|
||||
WorkData workData = new WorkData();
|
||||
workData.poule = o;
|
||||
return workData;
|
||||
})
|
||||
.call(o -> Panache.withTransaction(() -> repository.persist(o.poule)))
|
||||
.call(o -> (data.getMatches() == null || data.getMatches().isEmpty()) ? Uni.createFrom().nullItem() :
|
||||
Uni.createFrom()
|
||||
.item(data.getMatches().stream().flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id())
|
||||
.filter(Objects::nonNull)).distinct().toList())
|
||||
.chain(ids -> ids.isEmpty() ? Uni.createFrom().nullItem()
|
||||
: combRepository.list("id IN ?1", ids)
|
||||
.invoke(o2 -> o2.forEach(m -> o.membres.put(m.getId(), m)))
|
||||
)
|
||||
)
|
||||
.invoke(in -> {
|
||||
ArrayList<TreeModel> node = new ArrayList<>();
|
||||
for (TreeModel treeModel : in.poule.getTree())
|
||||
flatTreeChild(treeModel, node);
|
||||
|
||||
ArrayList<TreeData> new_node = new ArrayList<>();
|
||||
for (TreeData treeModel : data.getTrees())
|
||||
flatTreeChild(treeModel, new_node);
|
||||
|
||||
in.toRmNode = node.stream().filter(m -> new_node.stream()
|
||||
.noneMatch(m2 -> m2.getMatch().equals(m.getMatch().getSystemId())))
|
||||
.map(TreeModel::getId).toList();
|
||||
|
||||
in.unlinkNode = node;
|
||||
in.unlinkNode.forEach(n -> {
|
||||
n.setRight(null);
|
||||
n.setLeft(null);
|
||||
});
|
||||
|
||||
in.toRmMatch = in.poule.getMatchs().stream()
|
||||
.filter(m -> data.getMatches().stream().noneMatch(m2 -> m2.getId().equals(m.getSystemId())))
|
||||
.map(MatchModel::getId).toList();
|
||||
})
|
||||
.call(in -> in.unlinkNode.isEmpty() ? Uni.createFrom().nullItem() :
|
||||
Panache.withTransaction(() -> treeRepository.persist(in.unlinkNode)))
|
||||
.call(in -> in.toRmNode.isEmpty() ? Uni.createFrom().nullItem() :
|
||||
Panache.withTransaction(() -> treeRepository.delete("id IN ?1", in.toRmNode)))
|
||||
.call(in -> in.toRmMatch.isEmpty() ? Uni.createFrom().nullItem() :
|
||||
Panache.withTransaction(() -> Uni.join().all(
|
||||
in.toRmMatch.stream().map(l -> matchRepository.deleteById(l)).toList())
|
||||
.andCollectFailures()))
|
||||
.call(in -> data.getMatches().isEmpty() ? Uni.createFrom().nullItem() :
|
||||
Uni.join().all(
|
||||
data.getMatches().stream().map(m -> {
|
||||
MatchModel mm = findMatch(in.poule.getMatchs(), m.getId());
|
||||
if (mm == null) {
|
||||
mm = new MatchModel();
|
||||
mm.setId(null);
|
||||
mm.setSystem(system);
|
||||
mm.setSystemId(m.getId());
|
||||
}
|
||||
mm.setPoule(in.poule);
|
||||
mm.setPoule_ord(m.getPoule_ord());
|
||||
mm.setC1_str(m.getC1_str());
|
||||
mm.setC2_str(m.getC2_str());
|
||||
mm.setC1_id(in.membres.getOrDefault(m.getC1_id(), null));
|
||||
mm.setC2_id(in.membres.getOrDefault(m.getC2_id(), null));
|
||||
mm.setEnd(m.isEnd());
|
||||
mm.setGroupe(m.getGroupe());
|
||||
mm.getScores().clear();
|
||||
mm.getScores().addAll(m.getScores());
|
||||
|
||||
MatchModel finalMm = mm;
|
||||
return Panache.withTransaction(() -> matchRepository.persist(finalMm)
|
||||
.invoke(o -> in.match.add(o)));
|
||||
}).toList())
|
||||
.andCollectFailures())
|
||||
.call(in -> data.getTrees().isEmpty() ? Uni.createFrom().nullItem() :
|
||||
Uni.join().all(data.getTrees().stream()
|
||||
.map(m -> persisteTree(m, in.poule.getTree(), in.poule, in.match)).toList())
|
||||
.andCollectFailures())
|
||||
.map(__ -> "OK");
|
||||
}
|
||||
|
||||
private static class WorkData {
|
||||
PouleModel poule;
|
||||
HashMap<Long, MembreModel> membres = new HashMap<>();
|
||||
List<MatchModel> match = new ArrayList<>();
|
||||
List<Long> toRmMatch;
|
||||
List<TreeModel> unlinkNode;
|
||||
List<Long> toRmNode;
|
||||
}
|
||||
|
||||
public Uni<?> delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
|
||||
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
|
||||
.onItem().ifNull().failWith(() -> new RuntimeException("Poule not found"))
|
||||
.call(o -> permService.hasEditPerm(securityCtx, o.getCompet()))
|
||||
.call(o -> Mutiny.fetch(o.getMatchs()))
|
||||
.call(o -> Mutiny.fetch(o.getTree())
|
||||
.call(o2 -> o2.isEmpty() ? Uni.createFrom().nullItem() :
|
||||
Uni.createFrom().item(o2.stream().peek(m -> {
|
||||
m.setRight(null);
|
||||
m.setLeft(null);
|
||||
}).toList())
|
||||
.call(in -> Panache.withTransaction(() -> treeRepository.persist(in)))
|
||||
.map(in -> in.stream().map(TreeModel::getId).toList())
|
||||
.call(in -> in.isEmpty() ? Uni.createFrom().nullItem() :
|
||||
Panache.withTransaction(() -> treeRepository.delete("id IN ?1", in)))
|
||||
)
|
||||
)
|
||||
.call(o -> o.getMatchs().isEmpty() ? Uni.createFrom().nullItem() :
|
||||
Panache.withTransaction(() -> Uni.join().all(
|
||||
o.getMatchs().stream().map(l -> matchRepository.deleteById(l.getId())).toList())
|
||||
.andCollectFailures()))
|
||||
.chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId())));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
package fr.titionfire.ffsaf.domain.service;
|
||||
|
||||
public class TreeService {
|
||||
}
|
||||
@ -40,7 +40,7 @@ public class Client_Thread extends Thread {
|
||||
|
||||
private boolean isAuth;
|
||||
|
||||
private final HashMap<UUID, JsonConsumer<Object>> waitResult = new HashMap<>();
|
||||
private final HashMap<UUID, JsonConsumer<?>> waitResult = new HashMap<>();
|
||||
|
||||
public Client_Thread(ServerCustom serv, Socket s, PublicKey publicKey) throws IOException {
|
||||
this.serv = serv;
|
||||
@ -162,7 +162,7 @@ public class Client_Thread extends Thread {
|
||||
sendReq(object, type, null);
|
||||
}
|
||||
|
||||
public void sendReq(Object object, String code, JsonConsumer<Object> consumer) {
|
||||
public void sendReq(Object object, String code, JsonConsumer<?> consumer) {
|
||||
UUID uuid;
|
||||
do {
|
||||
uuid = UUID.randomUUID();
|
||||
|
||||
@ -22,6 +22,7 @@ public class SimpleClubModel {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new SimpleClubModel(model.getId(), model.getName(), model.getCountry(), model.getShieldURL());
|
||||
return new SimpleClubModel(model.getId(), model.getName(), model.getCountry(),
|
||||
"/api/club/" + model.getClubId() + "/logo");
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,12 +8,14 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
@Schema(hidden = true)
|
||||
public class SimpleCombModel {
|
||||
Long id;
|
||||
String lname = "";
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
package fr.titionfire.ffsaf.net2.data;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RegisterForReflection
|
||||
public record SimpleCompet(long id, String owner, boolean show_blason, boolean show_flag, List<UUID> admin, List<UUID> table) {
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
package fr.titionfire.ffsaf.net2.packet;
|
||||
|
||||
import fr.titionfire.ffsaf.ws.FileSocket;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
@ApplicationScoped
|
||||
public class RFile {
|
||||
private static final Logger LOGGER = Logger.getLogger(RFile.class);
|
||||
|
||||
final IAction requestSend = (client_Thread, message) -> {
|
||||
try {
|
||||
switch (message.data().get("type").asText()) {
|
||||
case "match":
|
||||
String code = UUID.randomUUID() + "-" + UUID.randomUUID();
|
||||
|
||||
FileSocket.FileRecv fileRecv = new FileSocket.FileRecv(null, message.data().get("name").asText(), null, null,
|
||||
System.currentTimeMillis());
|
||||
FileSocket.sessions.put(code, fileRecv);
|
||||
|
||||
client_Thread.sendRepTo(code, message);
|
||||
break;
|
||||
default:
|
||||
client_Thread.sendErrTo("", message);
|
||||
break;
|
||||
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
client_Thread.sendErrTo(e.getMessage(), message);
|
||||
}
|
||||
};
|
||||
|
||||
public static void register(HashMap<String, IAction> iMap) {
|
||||
RFile rFile = new RFile();
|
||||
|
||||
iMap.put("requestSend", rFile.requestSend);
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,5 @@ public class RegisterAction {
|
||||
|
||||
RComb.register(iMap);
|
||||
RClub.register(iMap);
|
||||
RFile.register(iMap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
package fr.titionfire.ffsaf.net2.request;
|
||||
|
||||
import fr.titionfire.ffsaf.net2.Client_Thread;
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleCompet;
|
||||
import fr.titionfire.ffsaf.utils.JsonConsumer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SReqCompet {
|
||||
|
||||
public static void sendUpdate(ArrayList<Client_Thread> client_Thread, SimpleCompet compet) {
|
||||
for (Client_Thread client : client_Thread) {
|
||||
client.sendNotify(compet, "sendConfig");
|
||||
}
|
||||
}
|
||||
|
||||
public static void getConfig(ArrayList<Client_Thread> client_Thread, long id_compet,
|
||||
CompletableFuture<SimpleCompet> future) {
|
||||
if (client_Thread.isEmpty()) return;
|
||||
client_Thread.get(0).sendReq(id_compet, "getConfig",
|
||||
new JsonConsumer<>(SimpleCompet.class, future::complete));
|
||||
}
|
||||
|
||||
public static void getAllHaveAccess(ArrayList<Client_Thread> client_Thread, String userId,
|
||||
CompletableFuture<HashMap<Long, String>> future) {
|
||||
if (client_Thread.isEmpty()) return;
|
||||
client_Thread.get(0).sendReq(userId, "getAllHaveAccess",
|
||||
new JsonConsumer<>(HashMap.class, future::complete));
|
||||
}
|
||||
|
||||
public static void rmCompet(ArrayList<Client_Thread> client_Thread, long id_compet) {
|
||||
for (Client_Thread client : client_Thread) {
|
||||
client.sendNotify(id_compet, "rmCompet");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,29 +1,94 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
|
||||
import fr.titionfire.ffsaf.domain.service.AffiliationService;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleAffiliation;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
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 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.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Tag(name = "Affiliation API", description = "API pour gérer les affiliations")
|
||||
@Path("api/affiliation")
|
||||
public class AffiliationEndpoints {
|
||||
|
||||
@Inject
|
||||
AffiliationService service;
|
||||
|
||||
@Inject
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
Consumer<Long> checkPerm = Unchecked.consumer(id -> {
|
||||
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(id))
|
||||
throw new DForbiddenException();
|
||||
});
|
||||
|
||||
@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<List<SimpleAffiliation>> getCurrentSaisonAffiliationAdmin() {
|
||||
return service.getCurrentSaisonAffiliation();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@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<List<SimpleAffiliation>> getAffiliation(
|
||||
@Parameter(description = "L'identifiant du club") @PathParam("id") long id) {
|
||||
return Uni.createFrom().item(id).invoke(checkPerm).chain(__ -> service.getAffiliation(id));
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("save")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
public Uni<String> saveAffRequest(AffiliationRequestForm form) {
|
||||
System.out.println(form);
|
||||
return Uni.createFrom().item("OK");
|
||||
@Path("{id}")
|
||||
@RolesAllowed("federation_admin")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@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<SimpleAffiliation> 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);
|
||||
}
|
||||
/*@POST
|
||||
@Path("affiliation")
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@RolesAllowed("federation_admin")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
public Uni<String> saveAffRequest(AffiliationRequestForm form) {
|
||||
System.out.println(form);
|
||||
return service.save(form);
|
||||
}*/
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,191 @@
|
||||
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.SecurityCtx;
|
||||
import fr.titionfire.ffsaf.utils.Utils;
|
||||
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.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
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
Consumer<Long> checkPerm = Unchecked.consumer(id -> {
|
||||
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(id))
|
||||
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<List<SimpleReqAffiliationResume>> 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<String> 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<SimpleReqAffiliation> 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 && !securityCtx.roleHas("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 && !securityCtx.roleHas("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<Response> 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<Response> 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());
|
||||
}
|
||||
}
|
||||
@ -1,53 +1,31 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.domain.service.AffiliationService;
|
||||
import fr.titionfire.ffsaf.rest.client.SirenService;
|
||||
import fr.titionfire.ffsaf.rest.data.UniteLegaleRoot;
|
||||
import fr.titionfire.ffsaf.rest.from.AffiliationRequestForm;
|
||||
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jodd.net.MimeTypes;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
@Path("api/asso")
|
||||
public class AssoEndpoints {
|
||||
|
||||
@RestClient
|
||||
SirenService sirenService;
|
||||
|
||||
@Inject
|
||||
AffiliationService service;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
@GET
|
||||
@Path("siren/{siren}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(hidden = true)
|
||||
public Uni<UniteLegaleRoot> getInfoSiren(@PathParam("siren") String siren) {
|
||||
return sirenService.get_unite(siren).onFailure().transform(throwable -> {
|
||||
if (throwable instanceof WebApplicationException exception) {
|
||||
if (exception.getResponse().getStatus() == 400)
|
||||
return new BadRequestException("Not found");
|
||||
return new DNotFoundException("Siret introuvable");
|
||||
}
|
||||
return throwable;
|
||||
});
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("affiliation")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
public Uni<String> saveAffRequest(AffiliationRequestForm form) {
|
||||
return service.save(form);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -25,10 +28,16 @@ public class AuthEndpoints {
|
||||
SecurityIdentity securityIdentity;
|
||||
|
||||
@Inject
|
||||
JsonWebToken accessToken;
|
||||
JsonWebToken IdToken;
|
||||
|
||||
@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,14 +46,21 @@ 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);
|
||||
return UserInfo.makeUserInfo(IdToken, securityIdentity);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/login")
|
||||
@Authenticated
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Operation(hidden = true)
|
||||
public Response login() throws URISyntaxException {
|
||||
return Response.temporaryRedirect(new URI(redirect)).build();
|
||||
}
|
||||
|
||||
@ -1,28 +1,318 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||
import fr.titionfire.ffsaf.domain.service.ClubService;
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
|
||||
import fr.titionfire.ffsaf.rest.data.DeskMember;
|
||||
import fr.titionfire.ffsaf.rest.data.RenewAffData;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleClub;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleClubList;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||
import fr.titionfire.ffsaf.rest.from.FullClubForm;
|
||||
import fr.titionfire.ffsaf.rest.from.PartClubForm;
|
||||
import fr.titionfire.ffsaf.utils.Contact;
|
||||
import fr.titionfire.ffsaf.utils.PageResult;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import fr.titionfire.ffsaf.utils.Utils;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.smallrye.mutiny.unchecked.Unchecked;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
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.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 {
|
||||
|
||||
@Inject
|
||||
ClubService clubService;
|
||||
|
||||
@Inject
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
Consumer<ClubModel> checkPerm = Unchecked.consumer(clubModel -> {
|
||||
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(clubModel.getId()))
|
||||
throw new DForbiddenException();
|
||||
});
|
||||
Consumer<Long> checkPerm2 = Unchecked.consumer(id -> {
|
||||
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(id))
|
||||
throw new DForbiddenException();
|
||||
});
|
||||
|
||||
@GET
|
||||
@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<List<SimpleClubModel>> getAll() {
|
||||
return clubService.getAll().map(clubModels -> clubModels.stream().map(SimpleClubModel::fromModel).toList());
|
||||
}
|
||||
|
||||
@GET
|
||||
@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<HashMap<String, String>> getConcatType() {
|
||||
return Uni.createFrom().item(Contact.toSite());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/find")
|
||||
@RolesAllowed({"federation_admin"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@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<PageResult<SimpleClubList>> 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)
|
||||
page = 1;
|
||||
return clubService.search(limit, page - 1, search, country);
|
||||
}
|
||||
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@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<SimpleClub> 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());
|
||||
});
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{id}")
|
||||
@RolesAllowed({"federation_admin"})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@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<String> 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);
|
||||
})).chain(() -> {
|
||||
if (input.getLogo().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getLogo(), media, "ppClub"
|
||||
)).invoke(Unchecked.consumer(out -> {
|
||||
if (!out.equals("OK"))
|
||||
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
|
||||
}));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
}).chain(() -> {
|
||||
if (input.getStatus().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getStatus(), media, "clubStatus"
|
||||
)).invoke(Unchecked.consumer(out -> {
|
||||
if (!out.equals("OK"))
|
||||
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
|
||||
}));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
});
|
||||
}
|
||||
|
||||
@PUT
|
||||
@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<Long> addAdminClub(FullClubForm input) {
|
||||
return clubService.add(input)
|
||||
.invoke(Unchecked.consumer(id -> {
|
||||
if (id == null) throw new InternalError("Fail to create club data");
|
||||
})).call(id -> {
|
||||
if (input.getLogo().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getLogo(), media, "ppClub"
|
||||
));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
}).call(id -> {
|
||||
if (input.getStatus().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getStatus(), media, "clubStatus"
|
||||
));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
});
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@RolesAllowed({"federation_admin"})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@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);
|
||||
}
|
||||
|
||||
@GET
|
||||
@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<SimpleClub> getOfUser() {
|
||||
return clubService.getOfUser(securityCtx).map(SimpleClub::fromModel)
|
||||
.invoke(m -> m.setContactMap(Contact.toSite()));
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/me")
|
||||
@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<String> setClubOfUser(PartClubForm form) {
|
||||
return clubService.updateOfUser(securityCtx, form);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/renew/{id}")
|
||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(hidden = true)
|
||||
public Uni<RenewAffData> getRenew(@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)
|
||||
.chain(__ -> clubService.getRenewData(id, List.of(m1_id, m2_id, m3_id)));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/desk/{id}")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@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<List<DeskMember>> getClubDesk(
|
||||
@Parameter(description = "Identifiant de club") @PathParam("id") long id) {
|
||||
return clubService.getClubDesk(checkPerm, id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{clubId}/logo")
|
||||
@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<Response> getLogo(
|
||||
@Parameter(description = "Identifiant long (clubId) de club") @PathParam("clubId") String clubId) {
|
||||
return clubService.getByClubId(clubId).chain(Unchecked.function(clubModel -> {
|
||||
try {
|
||||
return Utils.getMediaFile((clubModel != null) ? clubModel.getId() : -1, media, "ppClub",
|
||||
Uni.createFrom().nullItem());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}/status")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire"})
|
||||
@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<Response> 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",
|
||||
"statue-" + clubModel.getName() + ".pdf", Uni.createFrom().nullItem());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,249 +0,0 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.domain.service.MembreService;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||
import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
|
||||
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
||||
import fr.titionfire.ffsaf.utils.GroupeUtils;
|
||||
import fr.titionfire.ffsaf.utils.PageResult;
|
||||
import fr.titionfire.ffsaf.utils.Pair;
|
||||
import fr.titionfire.ffsaf.utils.Utils;
|
||||
import io.quarkus.oidc.IdToken;
|
||||
import io.quarkus.security.Authenticated;
|
||||
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.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jodd.net.MimeTypes;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Authenticated
|
||||
@Path("api/member")
|
||||
public class CombEndpoints {
|
||||
|
||||
@Inject
|
||||
MembreService membreService;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
@Inject
|
||||
@IdToken
|
||||
JsonWebToken idToken;
|
||||
|
||||
@Inject
|
||||
SecurityIdentity securityIdentity;
|
||||
|
||||
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
|
||||
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
|
||||
throw new ForbiddenException();
|
||||
});
|
||||
|
||||
@GET
|
||||
@Path("/find/admin")
|
||||
@RolesAllowed({"federation_admin"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<PageResult<SimpleMembre>> getFindAdmin(@QueryParam("limit") Integer limit,
|
||||
@QueryParam("page") Integer page,
|
||||
@QueryParam("search") String search,
|
||||
@QueryParam("club") String club) {
|
||||
if (limit == null)
|
||||
limit = 50;
|
||||
if (page == null || page < 1)
|
||||
page = 1;
|
||||
return membreService.searchAdmin(limit, page - 1, search, club);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/find/club")
|
||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<PageResult<SimpleMembre>> getFindClub(@QueryParam("limit") Integer limit,
|
||||
@QueryParam("page") Integer page,
|
||||
@QueryParam("search") String search) {
|
||||
if (limit == null)
|
||||
limit = 50;
|
||||
if (page == null || page < 1)
|
||||
page = 1;
|
||||
return membreService.search(limit, page - 1, search, idToken.getSubject());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<SimpleMembre> getById(@PathParam("id") long id) {
|
||||
return membreService.getById(id).onItem().invoke(checkPerm).map(SimpleMembre::fromModel);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{id}")
|
||||
@RolesAllowed({"federation_admin"})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
public Uni<String> setAdminMembre(@PathParam("id") long id, FullMemberForm input) {
|
||||
return membreService.update(id, input)
|
||||
.invoke(Unchecked.consumer(out -> {
|
||||
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
|
||||
})).chain(() -> {
|
||||
if (input.getPhoto_data().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||
)).invoke(Unchecked.consumer(out -> {
|
||||
if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out);
|
||||
}));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
});
|
||||
}
|
||||
|
||||
@POST
|
||||
@RolesAllowed({"federation_admin"})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
public Uni<Long> addAdminMembre(FullMemberForm input) {
|
||||
return membreService.add(input)
|
||||
.invoke(Unchecked.consumer(id -> {
|
||||
if (id == null) throw new InternalError("Fail to creat member data");
|
||||
})).call(id -> {
|
||||
if (input.getPhoto_data().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||
));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
});
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@RolesAllowed({"federation_admin"})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public Uni<String> deleteAdminMembre(@PathParam("id") long id) {
|
||||
return membreService.delete(id);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("club/{id}")
|
||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
public Uni<String> setMembre(@PathParam("id") long id, ClubMemberForm input) {
|
||||
return membreService.update(id, input, idToken, securityIdentity)
|
||||
.invoke(Unchecked.consumer(out -> {
|
||||
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
|
||||
})).chain(() -> {
|
||||
if (input.getPhoto_data().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||
)).invoke(Unchecked.consumer(out -> {
|
||||
if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out);
|
||||
}));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
});
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("club")
|
||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
public Uni<Long> addMembre(FullMemberForm input) {
|
||||
return membreService.add(input, idToken.getSubject())
|
||||
.invoke(Unchecked.consumer(id -> {
|
||||
if (id == null) throw new InternalError("Fail to creat member data");
|
||||
})).call(id -> {
|
||||
if (input.getPhoto_data().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||
));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
});
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("club/{id}")
|
||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public Uni<String> deleteMembre(@PathParam("id") long id) {
|
||||
return membreService.delete(id, idToken);
|
||||
}
|
||||
|
||||
private Future<String> replacePhoto(long id, byte[] input) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (InputStream is = new BufferedInputStream(new ByteArrayInputStream(input))) {
|
||||
String mimeType = URLConnection.guessContentTypeFromStream(is);
|
||||
String[] detectedExtensions = MimeTypes.findExtensionsByMimeTypes(mimeType, false);
|
||||
if (detectedExtensions.length == 0)
|
||||
throw new IOException("Fail to detect file extension for MIME type " + mimeType);
|
||||
|
||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id));
|
||||
File[] files = new File(media, "ppMembre").listFiles(filter);
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
String extension = "." + detectedExtensions[0];
|
||||
Files.write(new File(media, "ppMembre/" + id + extension).toPath(), input);
|
||||
return "OK";
|
||||
} catch (IOException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}/photo")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
public Uni<Response> getPhoto(@PathParam("id") long id) throws URISyntaxException {
|
||||
Future<Pair<File, byte[]>> future = CompletableFuture.supplyAsync(() -> {
|
||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id));
|
||||
File[] files = new File(media, "ppMembre").listFiles(filter);
|
||||
if (files != null && files.length > 0) {
|
||||
File file = files[0];
|
||||
try {
|
||||
byte[] data = Files.readAllBytes(file.toPath());
|
||||
return new Pair<>(file, data);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
URI uri = new URI("https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-chat/ava2.webp");
|
||||
|
||||
return membreService.getById(id).onItem().invoke(checkPerm).chain(__ -> Uni.createFrom().future(future)
|
||||
.map(filePair -> {
|
||||
if (filePair == null)
|
||||
return Response.temporaryRedirect(uri).build();
|
||||
|
||||
String mimeType = URLConnection.guessContentTypeFromName(filePair.getKey().getName());
|
||||
|
||||
Response.ResponseBuilder resp = Response.ok(filePair.getValue());
|
||||
resp.type(MediaType.APPLICATION_OCTET_STREAM);
|
||||
resp.header(HttpHeaders.CONTENT_LENGTH, filePair.getValue().length);
|
||||
resp.header(HttpHeaders.CONTENT_TYPE, mimeType);
|
||||
resp.header(HttpHeaders.CONTENT_DISPOSITION, "inline; ");
|
||||
|
||||
return resp.build();
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.domain.service.CompetitionService;
|
||||
import fr.titionfire.ffsaf.rest.data.CompetitionData;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleCompetData;
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Path("api/competition/")
|
||||
public class CompetitionEndpoints {
|
||||
|
||||
@Inject
|
||||
CompetitionService service;
|
||||
|
||||
@Inject
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<CompetitionData> getById(@PathParam("id") Long id) {
|
||||
return service.getById(securityCtx, id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}/safcaData")
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<SimpleCompetData> getSafcaData(@PathParam("id") Long id) {
|
||||
return service.getSafcaData(securityCtx, id);
|
||||
}
|
||||
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<List<CompetitionData>> getAll() {
|
||||
return service.getAll(securityCtx);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("all/{system}")
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<List<CompetitionData>> getAllSystem(@PathParam("system") CompetitionSystem system) {
|
||||
return service.getAllSystem(securityCtx, system);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<CompetitionData> addOrUpdate(CompetitionData data) {
|
||||
return service.addOrUpdate(securityCtx, data);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/safcaData")
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<?> setSafcaData(SimpleCompetData data) {
|
||||
return service.setSafcaData(securityCtx, data);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<?> delete(@PathParam("id") Long id) {
|
||||
return service.delete(securityCtx, id);
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,28 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.domain.service.KeycloakService;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.rest.from.MemberPermForm;
|
||||
import fr.titionfire.ffsaf.utils.GroupeUtils;
|
||||
import fr.titionfire.ffsaf.utils.Pair;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.vertx.mutiny.core.Vertx;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
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;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "Compte", description = "Gestion des comptes utilisateurs")
|
||||
@Path("api/compte")
|
||||
public class CompteEndpoints {
|
||||
|
||||
@ -23,10 +30,7 @@ public class CompteEndpoints {
|
||||
KeycloakService service;
|
||||
|
||||
@Inject
|
||||
JsonWebToken accessToken;
|
||||
|
||||
@Inject
|
||||
SecurityIdentity securityIdentity;
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
@Inject
|
||||
Vertx vertx;
|
||||
@ -34,11 +38,20 @@ public class CompteEndpoints {
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Operation(summary = "Renvoie les informations d'un compte utilisateur", description = "Renvoie les informations d'un" +
|
||||
" compte utilisateur en fonction de son identifiant long (UUID)")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Les informations du compte utilisateur"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le compte utilisateur n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<KeycloakService.UserCompteState> getCompte(@PathParam("id") String id) {
|
||||
return service.fetchCompte(id).call(pair -> vertx.getOrCreateContext().executeBlocking(() -> {
|
||||
if (!securityIdentity.getRoles().contains("federation_admin") && pair.getKey().groups().stream().map(GroupRepresentation::getPath)
|
||||
.noneMatch(s -> s.startsWith("/club/") && GroupeUtils.contains(s, accessToken)))
|
||||
throw new ForbiddenException();
|
||||
if (!securityCtx.roleHas("federation_admin") && pair.getKey().groups().stream()
|
||||
.map(GroupRepresentation::getPath)
|
||||
.noneMatch(s -> s.startsWith("/club/") && securityCtx.contains(s)))
|
||||
throw new DForbiddenException();
|
||||
return pair;
|
||||
})).map(Pair::getValue);
|
||||
}
|
||||
@ -46,6 +59,14 @@ public class CompteEndpoints {
|
||||
@PUT
|
||||
@Path("{id}/init")
|
||||
@RolesAllowed("federation_admin")
|
||||
@Operation(summary = "Initialise un compte utilisateur", description = "Initialise un compte utilisateur en fonction" +
|
||||
" de son identifiant")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "204", description = "Le compte utilisateur a été initialisé avec succès"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<?> initCompte(@PathParam("id") long id) {
|
||||
return service.initCompte(id);
|
||||
}
|
||||
@ -53,6 +74,7 @@ public class CompteEndpoints {
|
||||
@PUT
|
||||
@Path("{id}/setUUID/{nid}")
|
||||
@RolesAllowed("federation_admin")
|
||||
@Operation(hidden = true)
|
||||
public Uni<?> initCompte(@PathParam("id") long id, @PathParam("nid") String nid) {
|
||||
return service.setId(id, nid);
|
||||
}
|
||||
@ -60,13 +82,29 @@ public class CompteEndpoints {
|
||||
@GET
|
||||
@Path("{id}/roles")
|
||||
@RolesAllowed("federation_admin")
|
||||
public Uni<?> getRole(@PathParam("id") String id) {
|
||||
@Operation(summary = "Renvoie les rôles d'un compte utilisateur", description = "Renvoie les rôles d'un compte" +
|
||||
" utilisateur en fonction de son identifiant")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Les rôles du compte utilisateur"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le compte utilisateur n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<List<String>> getRole(@PathParam("id") String id) {
|
||||
return service.fetchRole(id);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{id}/roles")
|
||||
@RolesAllowed("federation_admin")
|
||||
@Operation(summary = "Met à jour les rôles d'un compte utilisateur", description = "Met à jour les rôles d'un compte" +
|
||||
" utilisateur en fonction de son identifiant et des rôles fournis dans le formulaire")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "204", description = "Les rôles du compte utilisateur ont été mis à jour avec succès"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le compte utilisateur n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<?> updateRole(@PathParam("id") String id, MemberPermForm form) {
|
||||
List<String> toAdd = new ArrayList<>();
|
||||
List<String> toRemove = new ArrayList<>();
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
@Path("api/countries")
|
||||
public class CountriesEndpoints {
|
||||
|
||||
@GET
|
||||
@Path("/{lang}/{code}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(hidden = true)
|
||||
public Uni<HashMap<String, String>> getCountries(@PathParam("lang") String lang, @PathParam("code") String code) {
|
||||
Locale locale = new Locale(lang, code);
|
||||
return Uni.createFrom().item(new HashMap<String, String>())
|
||||
.invoke(map -> {
|
||||
String[] locales = Locale.getISOCountries();
|
||||
for (String countryCode : locales) {
|
||||
if (countryCode.equals("AN"))
|
||||
continue;
|
||||
Locale obj = new Locale("", countryCode);
|
||||
map.put(countryCode, obj.getDisplayName(locale));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -3,17 +3,18 @@ package fr.titionfire.ffsaf.rest;
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.domain.service.LicenceService;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleLicence;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.rest.from.LicenceForm;
|
||||
import fr.titionfire.ffsaf.utils.GroupeUtils;
|
||||
import io.quarkus.oidc.IdToken;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
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 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.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@ -25,21 +26,25 @@ public class LicenceEndpoints {
|
||||
LicenceService licenceService;
|
||||
|
||||
@Inject
|
||||
@IdToken
|
||||
JsonWebToken idToken;
|
||||
|
||||
@Inject
|
||||
SecurityIdentity securityIdentity;
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
|
||||
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(membreModel.getClub().getId(), idToken))
|
||||
throw new ForbiddenException();
|
||||
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(membreModel.getClub().getId()))
|
||||
throw new DForbiddenException();
|
||||
});
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Renvoie les licences d'un membre", description = "Renvoie les licences d'un membre en fonction " +
|
||||
"de son identifiant")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "La liste des licences du membre"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<List<SimpleLicence>> getLicence(@PathParam("id") long id) {
|
||||
return licenceService.getLicence(id, checkPerm).map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
|
||||
}
|
||||
@ -48,6 +53,12 @@ public class LicenceEndpoints {
|
||||
@Path("current/admin")
|
||||
@RolesAllowed({"federation_admin"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Renvoie les licences de la saison en cours (pour les administrateurs)", description = "Renvoie" +
|
||||
" les licences de la saison en cours (pour les administrateurs)")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "La liste des licences de la saison en cours"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<List<SimpleLicence>> getCurrentSaisonLicenceAdmin() {
|
||||
return licenceService.getCurrentSaisonLicence(null).map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
|
||||
}
|
||||
@ -56,8 +67,14 @@ public class LicenceEndpoints {
|
||||
@Path("current/club")
|
||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Renvoie les licences de la saison en cours (pour les clubs)", description = "Renvoie les " +
|
||||
"licences de la saison en cours (pour les clubs)")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "La liste des licences de la saison en cours"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<List<SimpleLicence>> getCurrentSaisonLicenceClub() {
|
||||
return licenceService.getCurrentSaisonLicence(idToken).map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
|
||||
return licenceService.getCurrentSaisonLicence(securityCtx).map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
|
||||
}
|
||||
|
||||
@POST
|
||||
@ -65,6 +82,14 @@ public class LicenceEndpoints {
|
||||
@RolesAllowed("federation_admin")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@Operation(summary = "Créer une licence", description = "Créer unr licence en fonction de son identifiant et des " +
|
||||
"informations fournies dans le formulaire (pour les administrateurs)")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "La licence a été mise à jour avec succès"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "La licence n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<SimpleLicence> setLicence(@PathParam("id") long id, LicenceForm form) {
|
||||
return licenceService.setLicence(id, form).map(SimpleLicence::fromModel);
|
||||
}
|
||||
@ -73,6 +98,14 @@ public class LicenceEndpoints {
|
||||
@Path("{id}")
|
||||
@RolesAllowed("federation_admin")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Operation(summary = "Supprime une licence", description = "Supprime une licence en fonction de son identifiant " +
|
||||
"(pour les administrateurs)")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "204", description = "La licence a été supprimée avec succès"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "La licence n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<?> deleteLicence(@PathParam("id") long id) {
|
||||
return licenceService.deleteLicence(id);
|
||||
}
|
||||
@ -82,6 +115,14 @@ public class LicenceEndpoints {
|
||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@Operation(summary = "Demande une nouvelle licence", description = "Demande une nouvelle licence en fonction de" +
|
||||
" l'identifiant du membre et des informations fournies dans le formulaire (pour les clubs)")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "La demande de licence a été envoyée avec succès"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<SimpleLicence> askLicence(@PathParam("id") long id, LicenceForm form) {
|
||||
return licenceService.askLicence(id, form, checkPerm).map(SimpleLicence::fromModel);
|
||||
}
|
||||
@ -90,6 +131,14 @@ public class LicenceEndpoints {
|
||||
@Path("club/{id}")
|
||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Operation(summary = "Supprime une demande de licence", description = "Supprime une demande de licence en fonction " +
|
||||
"de son identifiant (pour les clubs)")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "204", description = "La demande de licence a été supprimée avec succès"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "La demande de licence n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<?> deleteAskLicence(@PathParam("id") long id) {
|
||||
return licenceService.deleteAskLicence(id, checkPerm);
|
||||
}
|
||||
|
||||
63
src/main/java/fr/titionfire/ffsaf/rest/MatchEndpoints.java
Normal file
63
src/main/java/fr/titionfire/ffsaf/rest/MatchEndpoints.java
Normal file
@ -0,0 +1,63 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.domain.service.MatchService;
|
||||
import fr.titionfire.ffsaf.rest.data.MatchData;
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Authenticated
|
||||
@Path("api/match/{system}/")
|
||||
public class MatchEndpoints {
|
||||
|
||||
@PathParam("system")
|
||||
private CompetitionSystem system;
|
||||
|
||||
@Inject
|
||||
MatchService service;
|
||||
|
||||
@Inject
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<MatchData> getById(@PathParam("id") Long id) {
|
||||
return service.getById(securityCtx, system, id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("getAllByPoule/{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<List<MatchData>> getAllByPoule(@PathParam("id") Long id) {
|
||||
return service.getAllByPoule(securityCtx, system, id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<MatchData> addOrUpdate(MatchData data) {
|
||||
return service.addOrUpdate(securityCtx, system, data);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("score/{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<?> updateScore(@PathParam("id") Long id, List<ScoreEmbeddable> scores) {
|
||||
return service.updateScore(securityCtx, system, id, scores);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<?> delete(@PathParam("id") Long id) {
|
||||
return service.delete(securityCtx, system, id);
|
||||
}
|
||||
}
|
||||
144
src/main/java/fr/titionfire/ffsaf/rest/MembreAdminEndpoints.java
Normal file
144
src/main/java/fr/titionfire/ffsaf/rest/MembreAdminEndpoints.java
Normal file
@ -0,0 +1,144 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.domain.service.MembreService;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
||||
import fr.titionfire.ffsaf.utils.PageResult;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import fr.titionfire.ffsaf.utils.Utils;
|
||||
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 org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
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.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Tag(name = "Membre admin", description = "Gestion des membres (pour les administrateurs)")
|
||||
@Path("api/member")
|
||||
@RolesAllowed({"federation_admin"})
|
||||
public class MembreAdminEndpoints {
|
||||
|
||||
@Inject
|
||||
MembreService membreService;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
@Inject
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
|
||||
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(membreModel.getClub().getId()))
|
||||
throw new DForbiddenException();
|
||||
});
|
||||
|
||||
@GET
|
||||
@Path("/find/admin")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Recherche des membres par critères ", description = "Recherche des membres en fonction de " +
|
||||
"critères tels que le nom, le prénom, le club, etc. ")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "La liste des membres correspondant aux critères de recherche"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<PageResult<SimpleMembre>> 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 = "Club à filter") @QueryParam("club") String club) {
|
||||
if (limit == null)
|
||||
limit = 50;
|
||||
if (page == null || page < 1)
|
||||
page = 1;
|
||||
return membreService.searchAdmin(limit, page - 1, search, club);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/find/similar")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(hidden = true)
|
||||
public Uni<List<SimpleMembre>> getSimilar(@QueryParam("fname") String fname, @QueryParam("lname") String lname) {
|
||||
return membreService.getSimilar(fname, lname);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@Operation(summary = "Met à jour les informations d'un membre en fonction de son identifiant", description = "Met à " +
|
||||
"jour les informations d'un membre en fonction de son identifiant")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Le membre a été mis à jour avec succès"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<String> setAdminMembre(
|
||||
@Parameter(description = "Identifiant de membre") @PathParam("id") long id, FullMemberForm input) {
|
||||
return membreService.update(id, input)
|
||||
.invoke(Unchecked.consumer(out -> {
|
||||
if (!out.equals("OK"))
|
||||
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
|
||||
})).chain(() -> {
|
||||
if (input.getPhoto_data().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||
)).invoke(Unchecked.consumer(out -> {
|
||||
if (!out.equals("OK"))
|
||||
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
|
||||
}));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
});
|
||||
}
|
||||
|
||||
@POST
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@Operation(summary = "Ajoute un nouveau membre", description = "Ajoute un nouveau membre avec les informations " +
|
||||
"fournies dans le formulaire")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Le membre 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<Long> addAdminMembre(FullMemberForm input) {
|
||||
return membreService.add(input)
|
||||
.invoke(Unchecked.consumer(id -> {
|
||||
if (id == null) throw new InternalError("Fail to creat member data");
|
||||
})).call(id -> {
|
||||
if (input.getPhoto_data().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||
));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
});
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Operation(summary = "Supprime un membre en fonction de son identifiant", description = "Supprime un membre en " +
|
||||
"fonction de son identifiant, ainsi que toutes les informations associées")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "204", description = "Le membre a été supprimé avec succès"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<String> deleteAdminMembre(
|
||||
@Parameter(description = "Identifiant de membre") @PathParam("id") long id) {
|
||||
return membreService.delete(id);
|
||||
}
|
||||
}
|
||||
126
src/main/java/fr/titionfire/ffsaf/rest/MembreClubEndpoints.java
Normal file
126
src/main/java/fr/titionfire/ffsaf/rest/MembreClubEndpoints.java
Normal file
@ -0,0 +1,126 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.domain.service.MembreService;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||
import fr.titionfire.ffsaf.rest.exception.DInternalError;
|
||||
import fr.titionfire.ffsaf.rest.from.ClubMemberForm;
|
||||
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
|
||||
import fr.titionfire.ffsaf.utils.PageResult;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import fr.titionfire.ffsaf.utils.Utils;
|
||||
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 org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
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;
|
||||
|
||||
@Tag(name = "Membre club", description = "Gestion des membres (pour les clubs)")
|
||||
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Path("api/member")
|
||||
public class MembreClubEndpoints {
|
||||
|
||||
@Inject
|
||||
MembreService membreService;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
@Inject
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
@GET
|
||||
@Path("/find/club")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Recherche des membres par critères", description = "Recherche des membres en " +
|
||||
"fonction de critères tels que le nom, le prénom, etc.")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "La liste des membres correspondant aux critères de recherche"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<PageResult<SimpleMembre>> getFindClub(
|
||||
@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) {
|
||||
if (limit == null)
|
||||
limit = 50;
|
||||
if (page == null || page < 1)
|
||||
page = 1;
|
||||
return membreService.search(limit, page - 1, search, securityCtx.getSubject());
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("club/{id}")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@Operation(summary = "Met à jour les informations d'un membre en fonction de son identifiant",
|
||||
description = "Met à jour les informations d'un membre en fonction de son identifiant")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Le membre a été mis à jour avec succès"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<String> setMembre(
|
||||
@Parameter(description = "Identifiant de membre") @PathParam("id") long id, ClubMemberForm input) {
|
||||
return membreService.update(id, input, securityCtx)
|
||||
.invoke(Unchecked.consumer(out -> {
|
||||
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
|
||||
})).chain(() -> {
|
||||
if (input.getPhoto_data().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||
)).invoke(Unchecked.consumer(out -> {
|
||||
if (!out.equals("OK"))
|
||||
throw new DInternalError("Impossible de reconnaitre le fichier: " + out);
|
||||
}));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
});
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("club")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@Operation(summary = "Ajoute un nouveau membre", description = "Ajoute un nouveau membre avec les informations " +
|
||||
"fournies dans le formulaire")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Le membre 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<Long> addMembre(FullMemberForm input) {
|
||||
return membreService.add(input, securityCtx.getSubject())
|
||||
.invoke(Unchecked.consumer(id -> {
|
||||
if (id == null) throw new InternalError("Fail to creat member data");
|
||||
})).call(id -> {
|
||||
if (input.getPhoto_data().length > 0)
|
||||
return Uni.createFrom().future(Utils.replacePhoto(id, input.getPhoto_data(), media, "ppMembre"
|
||||
));
|
||||
else
|
||||
return Uni.createFrom().nullItem();
|
||||
});
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("club/{id}")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Operation(summary = "Supprime un membre en fonction de son identifiant", description = "Supprime " +
|
||||
"un membre en fonction de son identifiant, ainsi que toutes les informations associées")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "204", description = "Le membre a été supprimé avec succès"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<String> deleteMembre(
|
||||
@Parameter(description = "Identifiant de membre") @PathParam("id") long id) {
|
||||
return membreService.delete(id, securityCtx);
|
||||
}
|
||||
}
|
||||
138
src/main/java/fr/titionfire/ffsaf/rest/MembreEndpoints.java
Normal file
138
src/main/java/fr/titionfire/ffsaf/rest/MembreEndpoints.java
Normal file
@ -0,0 +1,138 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.domain.service.MembreService;
|
||||
import fr.titionfire.ffsaf.rest.data.MeData;
|
||||
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
|
||||
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import fr.titionfire.ffsaf.utils.Utils;
|
||||
import io.quarkus.security.Authenticated;
|
||||
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.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.function.Consumer;
|
||||
|
||||
@Tag(name = "Membre", description = "Gestion des membres")
|
||||
@Authenticated
|
||||
@Path("api/member")
|
||||
public class MembreEndpoints {
|
||||
|
||||
@Inject
|
||||
MembreService membreService;
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
@Inject
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
|
||||
if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(membreModel.getClub().getId()))
|
||||
throw new DForbiddenException();
|
||||
});
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Renvoie les détails d'un membre en fonction de son identifiant", description = "Renvoie les " +
|
||||
"détails d'un membre en fonction de son identifiant")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Les détails du membre"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<SimpleMembre> getById(
|
||||
@Parameter(description = "Identifiant de membre") @PathParam("id") long id) {
|
||||
return membreService.getById(id).onItem().invoke(checkPerm).map(SimpleMembre::fromModel);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/find/licence")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Operation(summary = "Renvoie les détails d'un membre en fonction de son numéro de licence", description = "Renvoie " +
|
||||
"les détails d'un membre en fonction de son numéro de licence")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Les détails du membre"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<SimpleMembre> getByLicence(@QueryParam("id") long id) {
|
||||
return membreService.getByLicence(id).onItem().invoke(checkPerm).map(SimpleMembre::fromModel);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("me")
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Renvoie les informations du membre connecté", description = "Renvoie les informations du " +
|
||||
"membre connecté, y compris le club et les licences")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Les informations du membre connecté"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<MeData> getMe() {
|
||||
return membreService.getMembre(securityCtx.getSubject());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("me/licence")
|
||||
@Authenticated
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Renvoie l'attestation d'adhesion du membre connecté", description = "Renvoie l'attestation d'adhesion du " +
|
||||
"membre connecté, y compris le club et les licences")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "L'attestation d'adhesion"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'a pas de licence active"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<Response> getMeLicence() {
|
||||
return membreService.getLicencePdf(securityCtx.getSubject());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}/photo")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Operation(summary = "Renvoie la photo d'un membre", description = "Renvoie la photo d'un membre en fonction de son identifiant")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "La photo du membre"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas ou n'a pas de photo"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<Response> getPhoto(@PathParam("id") long id) throws URISyntaxException {
|
||||
return Utils.getMediaFile(id, media, "ppMembre", membreService.getById(id).onItem().invoke(checkPerm));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}/licence")
|
||||
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
|
||||
@Operation(summary = "Renvoie le pdf de la licence d'un membre", description = "Renvoie le pdf de la licence d'un membre en fonction de son identifiant")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Le pdf de la licence"),
|
||||
@APIResponse(responseCode = "403", description = "Accès refusé"),
|
||||
@APIResponse(responseCode = "404", description = "Le membre n'existe pas ou n'a pas de licence active"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Uni<Response> getLicencePDF(@PathParam("id") long id) {
|
||||
return membreService.getLicencePdf(membreService.getByIdWithLicence(id).onItem().invoke(checkPerm));
|
||||
}
|
||||
}
|
||||
63
src/main/java/fr/titionfire/ffsaf/rest/PouleEndpoints.java
Normal file
63
src/main/java/fr/titionfire/ffsaf/rest/PouleEndpoints.java
Normal file
@ -0,0 +1,63 @@
|
||||
package fr.titionfire.ffsaf.rest;
|
||||
|
||||
import fr.titionfire.ffsaf.domain.service.PouleService;
|
||||
import fr.titionfire.ffsaf.rest.data.PouleData;
|
||||
import fr.titionfire.ffsaf.rest.data.PouleFullData;
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import fr.titionfire.ffsaf.utils.SecurityCtx;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Authenticated
|
||||
@Path("api/poule/{system}/")
|
||||
public class PouleEndpoints {
|
||||
|
||||
@PathParam("system")
|
||||
private CompetitionSystem system;
|
||||
|
||||
@Inject
|
||||
PouleService service;
|
||||
|
||||
@Inject
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<PouleData> getById(@PathParam("id") Long id) {
|
||||
return service.getById(securityCtx, system, id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<List<PouleData>> getAll() {
|
||||
return service.getAll(securityCtx, system);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<PouleData> addOrUpdate(PouleData data) {
|
||||
return service.addOrUpdate(securityCtx, system, data);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("sync")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Uni<?> syncPoule(PouleFullData data) {
|
||||
return service.syncPoule(securityCtx, system, data);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Uni<?> delete(@PathParam("id") Long id) {
|
||||
return service.delete(securityCtx, system, id);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.CompetitionModel;
|
||||
import fr.titionfire.ffsaf.utils.CompetitionSystem;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class CompetitionData {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String uuid;
|
||||
private Date date;
|
||||
private CompetitionSystem system;
|
||||
private Long club;
|
||||
private String clubName;
|
||||
private String owner;
|
||||
|
||||
public static CompetitionData fromModel(CompetitionModel model) {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new CompetitionData(model.getId(), model.getName(), model.getUuid(), model.getDate(), model.getSystem(),
|
||||
model.getClub().getId(), model.getClub().getName(), model.getOwner());
|
||||
}
|
||||
}
|
||||
35
src/main/java/fr/titionfire/ffsaf/rest/data/DeskMember.java
Normal file
35
src/main/java/fr/titionfire/ffsaf/rest/data/DeskMember.java
Normal file
@ -0,0 +1,35 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
@Schema(name = "BureauMembre")
|
||||
public class DeskMember {
|
||||
@Schema(description = "Identifiant du membre", example = "1")
|
||||
private Long id;
|
||||
@Schema(description = "Nom du membre", example = "Doe")
|
||||
private String lname;
|
||||
@Schema(description = "Prénom du membre", example = "John")
|
||||
private String fname;
|
||||
@Schema(description = "Rôle du membre", example = "Président")
|
||||
private String role;
|
||||
|
||||
public static DeskMember fromModel(MembreModel membreModel) {
|
||||
if (membreModel == null)
|
||||
return null;
|
||||
|
||||
DeskMember deskMember = new DeskMember();
|
||||
deskMember.setId(membreModel.getId());
|
||||
deskMember.setLname(membreModel.getLname());
|
||||
deskMember.setFname(membreModel.getFname());
|
||||
deskMember.setRole(membreModel.getRole().toString());
|
||||
|
||||
return deskMember;
|
||||
}
|
||||
}
|
||||
36
src/main/java/fr/titionfire/ffsaf/rest/data/MatchData.java
Normal file
36
src/main/java/fr/titionfire/ffsaf/rest/data/MatchData.java
Normal file
@ -0,0 +1,36 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class MatchData {
|
||||
private Long id;
|
||||
private Long c1_id;
|
||||
private String c1_str;
|
||||
private Long c2_id;
|
||||
private String c2_str;
|
||||
private Long poule;
|
||||
private long poule_ord;
|
||||
private boolean isEnd = true;
|
||||
private char groupe;
|
||||
private List<ScoreEmbeddable> scores;
|
||||
|
||||
public static MatchData fromModel(MatchModel model) {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new MatchData(model.getSystemId(),
|
||||
(model.getC1_id() == null) ? null : model.getC1_id().getId(), model.getC1_str(),
|
||||
(model.getC2_id() == null) ? null : model.getC2_id().getId(), model.getC2_str(),
|
||||
model.getPoule().getId(), model.getPoule_ord(), model.isEnd(), model.getGroupe(),
|
||||
model.getScores());
|
||||
}
|
||||
}
|
||||
59
src/main/java/fr/titionfire/ffsaf/rest/data/MeData.java
Normal file
59
src/main/java/fr/titionfire/ffsaf/rest/data/MeData.java
Normal file
@ -0,0 +1,59 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class MeData {
|
||||
@Schema(description = "L'identifiant du membre.", example = "1")
|
||||
private long id;
|
||||
@Schema(description = "Le nom du membre.", example = "Dupont")
|
||||
private String lname = "";
|
||||
@Schema(description = "Le prénom du membre.", example = "Jean")
|
||||
private String fname = "";
|
||||
@Schema(description = "La catégorie du membre.", example = "SENIOR")
|
||||
private String categorie;
|
||||
@Schema(description = "Le nom du club du membre.", example = "Association sportive")
|
||||
private String club;
|
||||
@Schema(description = "Le genre du membre.", example = "Homme")
|
||||
private String genre;
|
||||
@Schema(description = "Le numéro de licence du membre.", example = "12345")
|
||||
private int licence;
|
||||
@Schema(description = "Le pays du membre.", example = "FR")
|
||||
private String country;
|
||||
@Schema(description = "La date de naissance du membre.")
|
||||
private Date birth_date;
|
||||
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com")
|
||||
private String email;
|
||||
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE")
|
||||
private String role;
|
||||
@Schema(description = "Le grade d'arbitrage du membre.", example = "N/A")
|
||||
private String grade_arbitrage;
|
||||
@Schema(description = "La liste des licences du membre.")
|
||||
private List<SimpleLicence> licences;
|
||||
|
||||
public void setMembre(MembreModel membreModel) {
|
||||
this.id = membreModel.getId();
|
||||
this.lname = membreModel.getLname();
|
||||
this.fname = membreModel.getFname();
|
||||
this.categorie = membreModel.getCategorie().getName();
|
||||
this.club = membreModel.getClub().getName();
|
||||
this.genre = membreModel.getGenre().str;
|
||||
this.licence = membreModel.getLicence();
|
||||
this.country = membreModel.getCountry();
|
||||
this.birth_date = membreModel.getBirth_date();
|
||||
this.email = membreModel.getEmail();
|
||||
this.role = membreModel.getRole().str;
|
||||
this.grade_arbitrage = membreModel.getGrade_arbitrage().str;
|
||||
}
|
||||
}
|
||||
23
src/main/java/fr/titionfire/ffsaf/rest/data/PouleData.java
Normal file
23
src/main/java/fr/titionfire/ffsaf/rest/data/PouleData.java
Normal file
@ -0,0 +1,23 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.PouleModel;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class PouleData {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Long compet;
|
||||
private Integer type;
|
||||
|
||||
public static PouleData fromModel(PouleModel model) {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new PouleData(model.getSystemId(), model.getName(), model.getCompet().getId(), model.getType());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class PouleFullData {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Long compet;
|
||||
private Integer type;
|
||||
private List<MatchData> matches;
|
||||
private List<TreeData> trees;
|
||||
|
||||
/*public static PouleFullData fromModel(PouleModel pouleModel) {
|
||||
if (pouleModel == null)
|
||||
return null;
|
||||
|
||||
PouleEntity pouleEntity = PouleEntity.fromModel(pouleModel);
|
||||
|
||||
return new PouleFullData(pouleEntity.getId(), pouleEntity.getName(), pouleEntity.getCompet().getId(),
|
||||
pouleEntity.getType(), pouleModel.getMatchs().stream().map(MatchData::fromModel).toList(),
|
||||
pouleEntity.getTrees().stream().map(TreeData::fromEntity).toList());
|
||||
}*/
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.MembreModel;
|
||||
import fr.titionfire.ffsaf.utils.RoleAsso;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class RenewAffData {
|
||||
String name;
|
||||
Long siret;
|
||||
String rna;
|
||||
String address;
|
||||
int saison;
|
||||
List<RenewMember> members;
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public static class RenewMember {
|
||||
String lname;
|
||||
String fname;
|
||||
String email;
|
||||
int licence;
|
||||
RoleAsso role;
|
||||
|
||||
public RenewMember(MembreModel o) {
|
||||
this.lname = o.getLname();
|
||||
this.fname = o.getFname();
|
||||
this.email = o.getEmail();
|
||||
this.licence = o.getLicence();
|
||||
this.role = o.getRole();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.AffiliationModel;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class SimpleAffiliation {
|
||||
@Schema(description = "L'identifiant de l'affiliation.", example = "1")
|
||||
private Long id;
|
||||
@Schema(description = "L'identifiant du club associé à l'affiliation.", example = "123")
|
||||
private Long club;
|
||||
@Schema(description = "La saison de l'affiliation.", example = "2022")
|
||||
private int saison;
|
||||
@Schema(description = "Indique si l'affiliation est validée ou non.", example = "true")
|
||||
private boolean validate;
|
||||
|
||||
public static SimpleAffiliation fromModel(AffiliationModel model) {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new SimpleAffiliationBuilder()
|
||||
.id(model.getId())
|
||||
.club(model.getClub().getId())
|
||||
.saison(model.getSaison())
|
||||
.validate(true)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
70
src/main/java/fr/titionfire/ffsaf/rest/data/SimpleClub.java
Normal file
70
src/main/java/fr/titionfire/ffsaf/rest/data/SimpleClub.java
Normal file
@ -0,0 +1,70 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||
import fr.titionfire.ffsaf.utils.Contact;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@ToString
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class SimpleClub {
|
||||
@Schema(description = "L'identifiant unique du club.", example = "1")
|
||||
private Long id;
|
||||
@Schema(description = "Identifiant long du club (UUID)", example = "b94f3167-3f6a-449c-a73b-ec84202bf07e")
|
||||
private String clubId;
|
||||
@Schema(description = "Le nom du club.", example = "Association sportive")
|
||||
private String name;
|
||||
@Schema(description = "Le pays du club.", example = "FR")
|
||||
private String country;
|
||||
@Schema(description = "Les contacts du club", example = "{\"SITE\": \"www.test.com\", \"COURRIEL\": \"test@test.com\"}")
|
||||
private Map<Contact, String> contact;
|
||||
@Schema(description = "Liste des lieux d'entraînement", example = "[{\"text\":\"addr 1\",\"lng\":2.24654,\"lat\":52.4868658},{\"text\":\"addr 2\",\"lng\":2.88654,\"lat\":52.7865456}]")
|
||||
private String training_location;
|
||||
@Schema(description = "Liste des jours et horaires d'entraînement (jours 0-6, 0=>lundi) (temps en minute depuis 00:00, 122=>2h02)", example = "[{\"day\":0,\"time_start\":164,\"time_end\":240},{\"day\":3,\"time_start\":124,\"time_end\":250}]")
|
||||
private String training_day_time;
|
||||
@Schema(description = "Contact interne du club", example = "john.doe@test.com")
|
||||
private String contact_intern;
|
||||
@Schema(description = "Adresse postale du club", example = "1 rue de l'exemple, 75000 Paris")
|
||||
private String address;
|
||||
@Schema(description = "RNA du club", example = "W123456789")
|
||||
private String RNA;
|
||||
@Schema(description = "Numéro SIRET du club", example = "12345678901234")
|
||||
private Long SIRET;
|
||||
@Schema(description = "Numéro d'affiliation du club", example = "12345")
|
||||
private Long no_affiliation;
|
||||
@Schema(description = "Club international", example = "false")
|
||||
private boolean international;
|
||||
@Schema(description = "Une map contenant les contacts possible pout un club.")
|
||||
private HashMap<String, String> contactMap = null;
|
||||
|
||||
public static SimpleClub fromModel(ClubModel model) {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new SimpleClubBuilder()
|
||||
.id(model.getId())
|
||||
.clubId(model.getClubId())
|
||||
.name(model.getName())
|
||||
.country(model.getCountry())
|
||||
.contact(model.getContact())
|
||||
.training_location(model.getTraining_location())
|
||||
.training_day_time(model.getTraining_day_time())
|
||||
.contact_intern(model.getContact_intern())
|
||||
.RNA(model.getRNA())
|
||||
.SIRET(model.getSIRET())
|
||||
.no_affiliation(model.getNo_affiliation())
|
||||
.international(model.isInternational())
|
||||
.address(model.getAddress())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.ClubModel;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class SimpleClubList {
|
||||
@Schema(description = "Identifiant du club", example = "1")
|
||||
Long id;
|
||||
@Schema(description = "Nom du club", example = "Club de test")
|
||||
String name;
|
||||
@Schema(description = "Pays du club", example = "FR")
|
||||
String country;
|
||||
@Schema(description = "Numéro SIRET du club", example = "12345678901234")
|
||||
Long siret;
|
||||
@Schema(description = "Numéro d'affiliation du club", example = "12345")
|
||||
Long no_affiliation;
|
||||
|
||||
public static SimpleClubList fromModel(ClubModel model) {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new SimpleClubList(model.getId(), model.getName(), model.getCountry(), model.getSIRET(),
|
||||
model.getNo_affiliation());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.net2.data.SimpleCompet;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class SimpleCompetData {
|
||||
private long id;
|
||||
private boolean show_blason;
|
||||
private boolean show_flag;
|
||||
private List<String> admin;
|
||||
private List<String> table;
|
||||
|
||||
public static SimpleCompetData fromModel(SimpleCompet compet) {
|
||||
if (compet == null)
|
||||
return null;
|
||||
|
||||
return new SimpleCompetData(compet.id(), compet.show_blason(), compet.show_flag(),
|
||||
new ArrayList<>(), new ArrayList<>());
|
||||
}
|
||||
}
|
||||
@ -5,16 +5,22 @@ import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class SimpleLicence {
|
||||
@Schema(description = "ID de la licence", example = "1")
|
||||
Long id;
|
||||
@Schema(description = "ID du membre", example = "1")
|
||||
Long membre;
|
||||
@Schema(description = "Saison de la licence", example = "2024")
|
||||
int saison;
|
||||
boolean certificate;
|
||||
@Schema(description = "Nom du médecin sur certificat médical.", example = "M. Jean")
|
||||
String certificate;
|
||||
@Schema(description = "Validation de la licence", example = "true")
|
||||
boolean validate;
|
||||
|
||||
public static SimpleLicence fromModel(LicenceModel model) {
|
||||
@ -25,7 +31,7 @@ public class SimpleLicence {
|
||||
.id(model.getId())
|
||||
.membre(model.getMembre().getId())
|
||||
.saison(model.getSaison())
|
||||
.certificate(model.isCertificate())
|
||||
.certificate(model.getCertificate())
|
||||
.validate(model.isValidate())
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ -18,19 +19,33 @@ import java.util.Date;
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class SimpleMembre {
|
||||
@Schema(description = "L'identifiant du membre.", example = "1")
|
||||
private long id;
|
||||
@Schema(description = "L'identifiant long du membre (userID).", example = "e81d1d35-d897-421e-8086-6c5e74d13c6e")
|
||||
private String userId;
|
||||
@Schema(description = "Le nom du membre.", example = "Dupont")
|
||||
private String lname = "";
|
||||
@Schema(description = "Le prénom du membre.", example = "Jean")
|
||||
private String fname = "";
|
||||
@Schema(description = "La catégorie du membre.", example = "SENIOR")
|
||||
private Categorie categorie;
|
||||
@Schema(description = "Le club du membre.")
|
||||
private SimpleClubModel club;
|
||||
@Schema(description = "Le genre du membre.", example = "H")
|
||||
private Genre genre;
|
||||
@Schema(description = "Le numéro de licence du membre.", example = "12345")
|
||||
private int licence;
|
||||
@Schema(description = "Le pays du membre.", example = "FR")
|
||||
private String country;
|
||||
@Schema(description = "La date de naissance du membre.")
|
||||
private Date birth_date;
|
||||
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com")
|
||||
private String email;
|
||||
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE")
|
||||
private RoleAsso role;
|
||||
@Schema(description = "Le grade d'arbitrage du membre.", example = "N/A")
|
||||
private GradeArbitrage grade_arbitrage;
|
||||
@Schema(hidden = true)
|
||||
private String url_photo;
|
||||
|
||||
public static SimpleMembre fromModel(MembreModel model) {
|
||||
|
||||
@ -0,0 +1,76 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.AffiliationRequestModel;
|
||||
import fr.titionfire.ffsaf.utils.RoleAsso;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class SimpleReqAffiliation {
|
||||
@Schema(description = "Identifiant de la demande d'affiliation", example = "1")
|
||||
Long id;
|
||||
@Schema(description = "Identifiant du club", example = "1")
|
||||
Long club;
|
||||
@Schema(description = "Nom du club si club similar trouver (même siret)", example = "Association sportive")
|
||||
String club_name;
|
||||
@Schema(description = "Identifiant du club affilié", example = "1")
|
||||
Long club_no_aff;
|
||||
@Schema(description = "Nom du club demander", example = "Association sportive")
|
||||
String name;
|
||||
@Schema(description = "Numéro SIRET de l'association", example = "12345678901234")
|
||||
long siret;
|
||||
@Schema(description = "Numéro RNA de l'association", example = "W123456789")
|
||||
String RNA;
|
||||
@Schema(description = "Adresse de l'association", example = "1 rue de l'exemple, 75000 Paris")
|
||||
String address;
|
||||
@Schema(description = "Liste des membres pour la demande d'affiliation")
|
||||
List<AffiliationMember> members;
|
||||
@Schema(description = "Saison de l'affiliation", example = "2025")
|
||||
int saison;
|
||||
|
||||
public static SimpleReqAffiliation fromModel(AffiliationRequestModel model) {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new SimpleReqAffiliation.SimpleReqAffiliationBuilder()
|
||||
.id(model.getId())
|
||||
.name(model.getName())
|
||||
.siret(model.getSiret())
|
||||
.RNA(model.getRNA())
|
||||
.address(model.getAddress())
|
||||
.saison(model.getSaison())
|
||||
.members(List.of(
|
||||
new AffiliationMember(model.getM1_lname(), model.getM1_fname(), model.getM1_email(),
|
||||
model.getM1_lincence(), model.getM1_role()),
|
||||
new AffiliationMember(model.getM2_lname(), model.getM2_fname(), model.getM2_email(),
|
||||
model.getM2_lincence(), model.getM2_role()),
|
||||
new AffiliationMember(model.getM3_lname(), model.getM3_fname(), model.getM3_email(),
|
||||
model.getM3_lincence(), model.getM3_role())
|
||||
))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public static class AffiliationMember {
|
||||
@Schema(description = "Nom du membre", example = "Doe")
|
||||
String lname;
|
||||
@Schema(description = "Prénom du membre", example = "John")
|
||||
String fname;
|
||||
@Schema(description = "Email du membre", example = "john.doe@test.com")
|
||||
String email;
|
||||
@Schema(description = "Numéro de licence du membre", example = "12345")
|
||||
int licence;
|
||||
@Schema(description = "Rôle du membre", example = "MEMBRE")
|
||||
RoleAsso role;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.AffiliationRequestModel;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class SimpleReqAffiliationResume {
|
||||
@Schema(description = "L'identifiant de la demande d'affiliation.", example = "1")
|
||||
Long id;
|
||||
@Schema(description = "Le nom de l'association.", example = "Association sportive")
|
||||
String name;
|
||||
@Schema(description = "Le numéro SIRET de l'association.", example = "12345678901234")
|
||||
long siret;
|
||||
@Schema(description = "La saison de l'affiliation.", example = "2025")
|
||||
int saison;
|
||||
|
||||
public static SimpleReqAffiliationResume fromModel(AffiliationRequestModel model) {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new SimpleReqAffiliationResume.SimpleReqAffiliationResumeBuilder()
|
||||
.id(model.getId())
|
||||
.name(model.getName())
|
||||
.siret(model.getSiret())
|
||||
.saison(model.getSaison())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
26
src/main/java/fr/titionfire/ffsaf/rest/data/TreeData.java
Normal file
26
src/main/java/fr/titionfire/ffsaf/rest/data/TreeData.java
Normal file
@ -0,0 +1,26 @@
|
||||
package fr.titionfire.ffsaf.rest.data;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.TreeModel;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public class TreeData {
|
||||
private Long id;
|
||||
private Long poule;
|
||||
private Integer level;
|
||||
private Long match;
|
||||
private TreeData left;
|
||||
private TreeData right;
|
||||
|
||||
public static TreeData fromModel(TreeModel model) {
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new TreeData(model.getId(), model.getPoule(), model.getLevel(), model.getMatch().getId(),
|
||||
fromModel(model.getLeft()), fromModel(model.getRight()));
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import io.quarkus.security.identity.SecurityIdentity;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -14,14 +15,23 @@ import java.util.Set;
|
||||
@Builder
|
||||
@RegisterForReflection
|
||||
public class UserInfo {
|
||||
@Schema(description = "L'identifiant de l'utilisateur.", example = "1234567890")
|
||||
String id;
|
||||
@Schema(description = "Le nom complet de l'utilisateur.", example = "John Doe")
|
||||
String name;
|
||||
@Schema(description = "Le prénom de l'utilisateur.", example = "John")
|
||||
String givenName;
|
||||
@Schema(description = "Le nom de famille de l'utilisateur.", example = "Doe")
|
||||
String familyName;
|
||||
@Schema(description = "L'adresse e-mail de l'utilisateur.", example = "jihn.doe@test.fr")
|
||||
String email;
|
||||
@Schema(description = "L'adresse e-mail de l'utilisateur a été vérifiée.", example = "true")
|
||||
boolean emailVerified;
|
||||
@Schema(description = "La date d'expiration du token d'accès.")
|
||||
long expiration;
|
||||
@Schema(description = "La liste des groupes de l'utilisateur.")
|
||||
List<String> groups;
|
||||
@Schema(description = "La liste des rôles de l'utilisateur.")
|
||||
Set<String> roles;
|
||||
|
||||
public static UserInfo makeUserInfo(JsonWebToken accessToken, SecurityIdentity securityIdentity) {
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package fr.titionfire.ffsaf.rest.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
public class DBadRequestException extends DetailException {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 7518556311032332135L;
|
||||
|
||||
public DBadRequestException(String message) {
|
||||
super(Response.Status.BAD_REQUEST, message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package fr.titionfire.ffsaf.rest.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
public class DForbiddenException extends DetailException {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 8408920537659758038L;
|
||||
|
||||
public DForbiddenException() {
|
||||
this("Accès a la ressource interdite");
|
||||
}
|
||||
|
||||
public DForbiddenException(String message) {
|
||||
super(Response.Status.FORBIDDEN, message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package fr.titionfire.ffsaf.rest.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
public class DInternalError extends DetailException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -3635595157694180842L;
|
||||
|
||||
public DInternalError(String message) {
|
||||
super(Response.Status.INTERNAL_SERVER_ERROR, message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package fr.titionfire.ffsaf.rest.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
public class DNotFoundException extends DetailException{
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -5193524134675732376L;
|
||||
|
||||
public DNotFoundException(String message) {
|
||||
super(Response.Status.NOT_FOUND, message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package fr.titionfire.ffsaf.rest.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@Getter
|
||||
public class DetailException extends Exception {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 5349921926328753676L;
|
||||
|
||||
private final Response.Status status;
|
||||
|
||||
public DetailException(Response.Status status, String message) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package fr.titionfire.ffsaf.rest.exception;
|
||||
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.ext.ExceptionMapper;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class DetailExceptionMapper implements ExceptionMapper<DetailException> {
|
||||
|
||||
@Override
|
||||
public Response toResponse(DetailException e) {
|
||||
return Response.status(e.getStatus())
|
||||
.entity(e.getMessage())
|
||||
.type(MediaType.TEXT_PLAIN_TYPE)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package fr.titionfire.ffsaf.rest.exception;
|
||||
|
||||
import fr.titionfire.ffsaf.utils.KeycloakException;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.ext.ExceptionMapper;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class KeycloakExceptionMapper implements ExceptionMapper<KeycloakException> {
|
||||
|
||||
@Override
|
||||
public Response toResponse(KeycloakException e) {
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("Erreur du gestionnaire d'identité: " + e.getMessage())
|
||||
.type(MediaType.TEXT_PLAIN_TYPE)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,86 +1,140 @@
|
||||
package fr.titionfire.ffsaf.rest.from;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.AffiliationRequestModel;
|
||||
import fr.titionfire.ffsaf.utils.RoleAsso;
|
||||
import jakarta.ws.rs.FormParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.jboss.resteasy.reactive.PartType;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class AffiliationRequestForm {
|
||||
@Schema(description = "L'identifiant de l'affiliation. (null si nouvelle demande d'affiliation)")
|
||||
@FormParam("id")
|
||||
private Long id = null;
|
||||
|
||||
@Schema(description = "Le nom de l'association.", example = "Association sportive", required = true)
|
||||
@FormParam("name")
|
||||
private String name = null;
|
||||
|
||||
@FormParam("siren")
|
||||
private String siren = null;
|
||||
@Schema(description = "Le numéro SIRET de l'association.", example = "12345678901234", required = true)
|
||||
@FormParam("siret")
|
||||
private Long siret = null;
|
||||
|
||||
@Schema(description = "Le numéro RNA de l'association. (peut être null)", example = "W123456789")
|
||||
@FormParam("rna")
|
||||
private String rna = null;
|
||||
|
||||
@Schema(description = "L'adresse de l'association.", example = "1 rue de l'exemple, 75000 Paris", required = true)
|
||||
@FormParam("adresse")
|
||||
private String adresse = null;
|
||||
|
||||
@Schema(description = "La saison de l'affiliation.", example = "2025", required = true)
|
||||
@FormParam("saison")
|
||||
private int saison = -1;
|
||||
|
||||
@Schema(description = "Le statut de l'association.", type = SchemaType.ARRAY, implementation = byte.class)
|
||||
@FormParam("status")
|
||||
@PartType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
private byte[] status = new byte[0];
|
||||
|
||||
@Schema(description = "Le logo de l'association.", type = SchemaType.ARRAY, implementation = byte.class)
|
||||
@FormParam("logo")
|
||||
@PartType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
private byte[] logo = new byte[0];
|
||||
|
||||
@FormParam("president-nom")
|
||||
private String president_lname = null;
|
||||
@FormParam("president-prenom")
|
||||
private String president_fname = null;
|
||||
@FormParam("president-mail")
|
||||
private String president_email = null;
|
||||
@FormParam("president-licence")
|
||||
private String president_lincence = null;
|
||||
@Schema(description = "Le nom du premier membre de l'association.", example = "Doe", required = true)
|
||||
@FormParam("m1_nom")
|
||||
private String m1_lname = null;
|
||||
|
||||
@FormParam("tresorier-nom")
|
||||
private String tresorier_lname = null;
|
||||
@FormParam("tresorier-prenom")
|
||||
private String tresorier_fname = null;
|
||||
@FormParam("tresorier-mail")
|
||||
private String tresorier_email = null;
|
||||
@FormParam("tresorier-licence")
|
||||
private String tresorier_lincence = null;
|
||||
@Schema(description = "Le prénom du premier membre de l'association.", example = "John", required = true)
|
||||
@FormParam("m1_prenom")
|
||||
private String m1_fname = null;
|
||||
|
||||
@FormParam("secretaire-nom")
|
||||
private String secretaire_lname = null;
|
||||
@FormParam("secretaire-prenom")
|
||||
private String secretaire_fname = null;
|
||||
@FormParam("secretaire-mail")
|
||||
private String secretaire_email = null;
|
||||
@FormParam("secretaire-licence")
|
||||
private String secretaire_lincence = null;
|
||||
@Schema(description = "L'adresse e-mail du premier membre de l'association.", example = "john.doe@test.com", required = true)
|
||||
@FormParam("m1_mail")
|
||||
private String m1_email = null;
|
||||
|
||||
@Schema(description = "Le numéro de licence du premier membre de l'association. (null si non licencié)", example = "12345")
|
||||
@FormParam("m1_licence")
|
||||
private String m1_lincence = null;
|
||||
|
||||
@Schema(description = "Le rôle du premier membre de l'association. (doit être PRESIDENT)", example = "PRESIDENT", required = true)
|
||||
@FormParam("m1_role")
|
||||
private RoleAsso m1_role = null;
|
||||
|
||||
@Schema(description = "Le nom du deuxième membre de l'association.", example = "Xavier", required = true)
|
||||
@FormParam("m2_nom")
|
||||
private String m2_lname = null;
|
||||
|
||||
@Schema(description = "Le prénom du deuxième membre de l'association.", example = "Login", required = true)
|
||||
@FormParam("m2_prenom")
|
||||
private String m2_fname = null;
|
||||
|
||||
@Schema(description = "L'adresse e-mail du deuxième membre de l'association.", example = "xavier.login@test.com", required = true)
|
||||
@FormParam("m2_mail")
|
||||
private String m2_email = null;
|
||||
|
||||
@Schema(description = "Le numéro de licence du deuxième membre de l'association. (null si non licencié)", example = "04242")
|
||||
@FormParam("m2_licence")
|
||||
private String m2_lincence = null;
|
||||
|
||||
@Schema(description = "Le rôle du deuxième membre de l'association.", example = "SECRETAIRE", required = true)
|
||||
@FormParam("m2_role")
|
||||
private RoleAsso m2_role = null;
|
||||
|
||||
@Schema(description = "Le nom du troisième membre de l'association.", example = "Doe2", required = true)
|
||||
@FormParam("m3_nom")
|
||||
private String m3_lname = null;
|
||||
|
||||
@Schema(description = "Le prénom du troisième membre de l'association.", example = "John2", required = true)
|
||||
@FormParam("m3_prenom")
|
||||
private String m3_fname = null;
|
||||
|
||||
@Schema(description = "L'adresse e-mail du troisième membre de l'association.", example = "john.doe22@test.com", required = true)
|
||||
@FormParam("m3_mail")
|
||||
private String m3_email = null;
|
||||
|
||||
@Schema(description = "Le numéro de licence du troisième membre de l'association. (null si non licencié)")
|
||||
@FormParam("m3_licence")
|
||||
private String m3_lincence = null;
|
||||
|
||||
@Schema(description = "Le rôle du troisième membre de l'association.", example = "MEMBREBUREAU", required = true)
|
||||
@FormParam("m3_role")
|
||||
private RoleAsso m3_role = null;
|
||||
|
||||
public AffiliationRequestModel toModel() {
|
||||
AffiliationRequestModel model = new AffiliationRequestModel();
|
||||
model.setName(this.getName());
|
||||
model.setSiren(this.getSiren());
|
||||
model.setSiret(this.getSiret());
|
||||
model.setRNA(this.getRna());
|
||||
model.setAddress(this.getAdresse());
|
||||
model.setSaison(this.getSaison());
|
||||
|
||||
model.setPresident_lname(this.getPresident_lname());
|
||||
model.setPresident_fname(this.getPresident_fname());
|
||||
model.setPresident_email(this.getPresident_email());
|
||||
model.setPresident_lincence((this.getPresident_lincence() == null || this.getPresident_lincence().isBlank())
|
||||
? 0 : Integer.parseInt(this.getPresident_lincence()));
|
||||
model.setM1_lname(this.getM1_lname());
|
||||
model.setM1_fname(this.getM1_fname());
|
||||
model.setM1_email(this.getM1_email());
|
||||
model.setM1_lincence((this.getM1_lincence() == null || this.getM1_lincence().isBlank())
|
||||
? -1 : Integer.parseInt(this.getM1_lincence()));
|
||||
model.setM1_role(this.getM1_role());
|
||||
|
||||
model.setTresorier_lname(this.getTresorier_lname());
|
||||
model.setTresorier_fname(this.getTresorier_fname());
|
||||
model.setTresorier_email(this.getTresorier_email());
|
||||
model.setTresorier_lincence((this.getPresident_lincence() == null || this.getPresident_lincence().isBlank())
|
||||
? 0 : Integer.parseInt(this.getTresorier_lincence()));
|
||||
model.setM2_lname(this.getM2_lname());
|
||||
model.setM2_fname(this.getM2_fname());
|
||||
model.setM2_email(this.getM2_email());
|
||||
model.setM2_lincence((this.getM1_lincence() == null || this.getM1_lincence().isBlank())
|
||||
? -1 : Integer.parseInt(this.getM2_lincence()));
|
||||
model.setM2_role(this.getM2_role());
|
||||
|
||||
model.setSecretaire_lname(this.getSecretaire_lname());
|
||||
model.setSecretaire_fname(this.getSecretaire_fname());
|
||||
model.setSecretaire_email(this.getSecretaire_email());
|
||||
model.setSecretaire_lincence((this.getPresident_lincence() == null || this.getPresident_lincence().isBlank())
|
||||
? 0 : Integer.parseInt(this.getSecretaire_lincence()));
|
||||
model.setM3_lname(this.getM3_lname());
|
||||
model.setM3_fname(this.getM3_fname());
|
||||
model.setM3_email(this.getM3_email());
|
||||
model.setM3_lincence((this.getM1_lincence() == null || this.getM1_lincence().isBlank())
|
||||
? -1 : Integer.parseInt(this.getM3_lincence()));
|
||||
model.setM3_role(this.getM3_role());
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
@ -0,0 +1,170 @@
|
||||
package fr.titionfire.ffsaf.rest.from;
|
||||
|
||||
import fr.titionfire.ffsaf.utils.RoleAsso;
|
||||
import jakarta.ws.rs.FormParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.jboss.resteasy.reactive.PartType;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class AffiliationRequestSaveForm {
|
||||
@Schema(description = "L'identifiant de l'affiliation.", example = "1", required = true)
|
||||
@FormParam("id")
|
||||
private Long id = null;
|
||||
|
||||
@Schema(description = "Le nom de l'association.", example = "Association sportive", required = true)
|
||||
@FormParam("name")
|
||||
private String name = null;
|
||||
|
||||
@Schema(description = "Le numéro SIRET de l'association.", example = "12345678901234", required = true)
|
||||
@FormParam("siret")
|
||||
private Long siret = null;
|
||||
|
||||
@Schema(description = "Le numéro RNA de l'association. (peut être null)", example = "W123456789")
|
||||
@FormParam("rna")
|
||||
private String rna = null;
|
||||
|
||||
@Schema(description = "L'adresse de l'association.", example = "1 rue de l'exemple, 75000 Paris", required = true)
|
||||
@FormParam("address")
|
||||
private String address = null;
|
||||
|
||||
@Schema(description = "Le statut de l'association.")
|
||||
@FormParam("status")
|
||||
@PartType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
private byte[] status = new byte[0];
|
||||
|
||||
@Schema(description = "Le logo de l'association.")
|
||||
@FormParam("logo")
|
||||
@PartType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
private byte[] logo = new byte[0];
|
||||
|
||||
@Schema(description = "Mode utiliser pour la sauvegarde du membre 1 (0 = licence mode, 2 = nom, prénom)", example = "0", required = true)
|
||||
@FormParam("m1_mode")
|
||||
private Integer m1_mode = null;
|
||||
|
||||
@Schema(description = "Le rôle du premier membre de l'association.", example = "PRÉSIDENT", required = true)
|
||||
@FormParam("m1_role")
|
||||
private RoleAsso m1_role = null;
|
||||
|
||||
@Schema(description = "Le numéro de licence du premier membre de l'association. (null si non licencié)", example = "1234567", required = true)
|
||||
@FormParam("m1_licence")
|
||||
private String m1_lincence = null;
|
||||
|
||||
@Schema(description = "Le nom du premier membre de l'association.", example = "Dupont", required = true)
|
||||
@FormParam("m1_lname")
|
||||
private String m1_lname = null;
|
||||
|
||||
@Schema(description = "Le prénom du premier membre de l'association.", example = "Jean", required = true)
|
||||
@FormParam("m1_fname")
|
||||
private String m1_fname = null;
|
||||
|
||||
@Schema(description = "L'adresse e-mail du premier membre de l'association.", example = "jean.dupont@example.com", required = true)
|
||||
@FormParam("m1_email")
|
||||
private String m1_email = null;
|
||||
|
||||
@Schema(name = "keep_email",
|
||||
description = "Conserver l'email de la base de donner (1 = conserve, 0 = replacer par 'm1_email')", example = "1", required = true)
|
||||
@FormParam("m1_email_mode")
|
||||
private Integer m1_email_mode = null;
|
||||
|
||||
@Schema(description = "Mode utiliser pour la sauvegarde du membre 2 (0 = licence mode, 2 = nom, prénom)", example = "0", required = true)
|
||||
@FormParam("m2_mode")
|
||||
private Integer m2_mode = null;
|
||||
|
||||
@Schema(description = "Le rôle du deuxième membre de l'association.", example = "TRÉSORIER", required = true)
|
||||
@FormParam("m2_role")
|
||||
private RoleAsso m2_role = null;
|
||||
|
||||
@Schema(description = "Le numéro de licence du deuxième membre de l'association. (null si non licencié)", example = "2345678", required = true)
|
||||
@FormParam("m2_licence")
|
||||
private String m2_lincence = null;
|
||||
|
||||
@Schema(description = "Le nom du deuxième membre de l'association.", example = "Durand", required = true)
|
||||
@FormParam("m2_lname")
|
||||
private String m2_lname = null;
|
||||
|
||||
@Schema(description = "Le prénom du deuxième membre de l'association.", example = "Paul", required = true)
|
||||
@FormParam("m2_fname")
|
||||
private String m2_fname = null;
|
||||
|
||||
@Schema(description = "L'adresse e-mail du deuxième membre de l'association.", example = "paul.durand@example.com", required = true)
|
||||
@FormParam("m2_email")
|
||||
private String m2_email = null;
|
||||
|
||||
@Schema(name = "keep_email",
|
||||
description = "Conserver l'email de la base de donner (1 = conserve, 0 = replacer par 'm2_email')", example = "1", required = true)
|
||||
@FormParam("m2_email_mode")
|
||||
private Integer m2_email_mode = null;
|
||||
|
||||
@Schema(description = "Mode utiliser pour la sauvegarde du membre 3 (0 = licence mode, 2 = nom, prénom)", example = "0", required = true)
|
||||
@FormParam("m3_mode")
|
||||
private Integer m3_mode = null;
|
||||
|
||||
@Schema(description = "Le rôle du troisième membre de l'association.", example = "SECRÉTAIRE", required = true)
|
||||
@FormParam("m3_role")
|
||||
private RoleAsso m3_role = null;
|
||||
|
||||
@Schema(description = "Le numéro de licence du troisième membre de l'association. (null si non licencié)", example = "3456789", required = true)
|
||||
@FormParam("m3_licence")
|
||||
private String m3_lincence = null;
|
||||
|
||||
@Schema(description = "Le nom du troisième membre de l'association.", example = "Martin", required = true)
|
||||
@FormParam("m3_lname")
|
||||
private String m3_lname = null;
|
||||
|
||||
@Schema(description = "Le prénom du troisième membre de l'association.", example = "Pierre", required = true)
|
||||
@FormParam("m3_fname")
|
||||
private String m3_fname = null;
|
||||
|
||||
@Schema(description = "L'adresse e-mail du troisième membre de l'association.", example = "pierre.martin@example.com", required = true)
|
||||
@FormParam("m3_email")
|
||||
private String m3_email = null;
|
||||
|
||||
@Schema(name = "keep_email",
|
||||
description = "Conserver l'email de la base de donner (1 = conserve, 0 = replacer par 'm3_email')", example = "1", required = true)
|
||||
@FormParam("m3_email_mode")
|
||||
private Integer m3_email_mode = null;
|
||||
|
||||
|
||||
@Getter
|
||||
public class Member {
|
||||
private Integer mode;
|
||||
private RoleAsso role;
|
||||
private String licence;
|
||||
private String lname;
|
||||
private String fname;
|
||||
private String email;
|
||||
private Integer email_mode;
|
||||
|
||||
public Member(int n) {
|
||||
if (n == 1) {
|
||||
mode = m1_mode;
|
||||
role = m1_role;
|
||||
licence = m1_lincence;
|
||||
lname = m1_lname;
|
||||
fname = m1_fname;
|
||||
email = m1_email;
|
||||
email_mode = m1_email_mode;
|
||||
} else if (n == 2) {
|
||||
mode = m2_mode;
|
||||
role = m2_role;
|
||||
licence = m2_lincence;
|
||||
lname = m2_lname;
|
||||
fname = m2_fname;
|
||||
email = m2_email;
|
||||
email_mode = m2_email_mode;
|
||||
} else if (n == 3) {
|
||||
mode = m3_mode;
|
||||
role = m3_role;
|
||||
licence = m3_lincence;
|
||||
lname = m3_lname;
|
||||
fname = m3_fname;
|
||||
email = m3_email;
|
||||
email_mode = m3_email_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,39 +6,50 @@ import fr.titionfire.ffsaf.utils.RoleAsso;
|
||||
import jakarta.ws.rs.FormParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.Getter;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.jboss.resteasy.reactive.PartType;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Getter
|
||||
public class ClubMemberForm {
|
||||
@Schema(description = "L'identifiant du membre.", example = "1234567", required = true)
|
||||
@FormParam("id")
|
||||
private String id = null;
|
||||
|
||||
@Schema(description = "Le nom du membre.", example = "Dupont", required = true)
|
||||
@FormParam("lname")
|
||||
private String lname = null;
|
||||
|
||||
@Schema(description = "Le prénom du membre.", example = "Jean", required = true)
|
||||
@FormParam("fname")
|
||||
private String fname = null;
|
||||
|
||||
@Schema(description = "La catégorie du membre.", example = "SENIOR", required = true)
|
||||
@FormParam("categorie")
|
||||
private Categorie categorie = null;
|
||||
|
||||
@Schema(description = "Le genre du membre.", example = "H", required = true)
|
||||
@FormParam("genre")
|
||||
private Genre genre;
|
||||
|
||||
@Schema(description = "Le pays du membre.", example = "FR", required = true)
|
||||
@FormParam("country")
|
||||
private String country;
|
||||
|
||||
@Schema(description = "La date de naissance du membre.", required = true)
|
||||
@FormParam("birth_date")
|
||||
private Date birth_date;
|
||||
|
||||
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com", required = true)
|
||||
@FormParam("email")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE", required = true)
|
||||
@FormParam("role")
|
||||
private RoleAsso role;
|
||||
|
||||
@Schema(description = "La photo du membre.")
|
||||
@FormParam("photo_data")
|
||||
@PartType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
private byte[] photo_data = new byte[0];
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
package fr.titionfire.ffsaf.rest.from;
|
||||
|
||||
import jakarta.ws.rs.FormParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.jboss.resteasy.reactive.PartType;
|
||||
|
||||
@ToString
|
||||
@Getter
|
||||
public class FullClubForm {
|
||||
@FormParam("id")
|
||||
@Schema(description = "Identifiant du club", example = "1", required = true)
|
||||
private String id = null;
|
||||
|
||||
@FormParam("name")
|
||||
@Schema(description = "Nom du club", example = "Association sportive", required = true)
|
||||
private String name = null;
|
||||
|
||||
@FormParam("country")
|
||||
@Schema(description = "Pays du club", example = "FR", required = true)
|
||||
private String country = null;
|
||||
|
||||
@FormParam("contact")
|
||||
@Schema(description = "Les contacts du club", example = "{\"SITE\": \"www.test.com\", \"COURRIEL\": \"test@test.com\"}", required = true)
|
||||
private String contact = null;
|
||||
|
||||
@FormParam("training_location")
|
||||
@Schema(description = "Liste des lieux d'entraînement", example = "[{\"text\":\"addr 1\",\"lng\":2.24654,\"lat\":52.4868658},{\"text\":\"addr 2\",\"lng\":2.88654,\"lat\":52.7865456}]", required = true)
|
||||
private String training_location = null;
|
||||
|
||||
@FormParam("training_day_time")
|
||||
@Schema(description = "Liste des jours et horaires d'entraînement (jours 0-6, 0=>lundi) (temps en minute depuis 00:00, 122=>2h02)", example = "[{\"day\":0,\"time_start\":164,\"time_end\":240},{\"day\":3,\"time_start\":124,\"time_end\":250}]", required = true)
|
||||
private String training_day_time = null;
|
||||
|
||||
@FormParam("contact_intern")
|
||||
@Schema(description = "Contact interne du club", example = "john.doe@test.com")
|
||||
private String contact_intern = null;
|
||||
|
||||
@FormParam("address")
|
||||
@Schema(description = "Adresse postale du club", example = "1 rue de l'exemple, 75000 Paris", required = true)
|
||||
private String address = null;
|
||||
|
||||
@FormParam("rna")
|
||||
@Schema(description = "RNA du club", example = "W123456789")
|
||||
private String rna = null;
|
||||
|
||||
@FormParam("siret")
|
||||
@Schema(description = "Numéro SIRET du club", example = "12345678901234", required = true)
|
||||
private String siret = null;
|
||||
|
||||
@FormParam("international")
|
||||
@Schema(description = "Club international", example = "false", required = true)
|
||||
private boolean international = false;
|
||||
|
||||
@FormParam("status")
|
||||
@PartType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
@Schema(description = "Le statut de l'association.", type = SchemaType.ARRAY, implementation = byte.class)
|
||||
private byte[] status = new byte[0];
|
||||
|
||||
@FormParam("logo")
|
||||
@PartType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
@Schema(description = "Le logo de l'association.", type = SchemaType.ARRAY, implementation = byte.class)
|
||||
private byte[] logo = new byte[0];
|
||||
}
|
||||
@ -7,48 +7,62 @@ import fr.titionfire.ffsaf.utils.RoleAsso;
|
||||
import jakarta.ws.rs.FormParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.Getter;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.jboss.resteasy.reactive.PartType;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Getter
|
||||
public class FullMemberForm {
|
||||
@Schema(description = "L'identifiant du membre.", example = "1")
|
||||
@FormParam("id")
|
||||
private String id = null;
|
||||
|
||||
@Schema(description = "Le nom du membre.", example = "Dupont")
|
||||
@FormParam("lname")
|
||||
private String lname = null;
|
||||
|
||||
@Schema(description = "Le prénom du membre.", example = "Jean")
|
||||
@FormParam("fname")
|
||||
private String fname = null;
|
||||
|
||||
@Schema(description = "La catégorie du membre.", example = "SENIOR")
|
||||
@FormParam("categorie")
|
||||
private Categorie categorie = null;
|
||||
|
||||
@Schema(description = "L'identifiant du club du membre.", example = "1")
|
||||
@FormParam("club")
|
||||
private Long club = null;
|
||||
|
||||
@Schema(description = "Le genre du membre.", example = "H")
|
||||
@FormParam("genre")
|
||||
private Genre genre;
|
||||
|
||||
@Schema(description = "Le numéro de licence du membre.", example = "12345")
|
||||
@FormParam("licence")
|
||||
private int licence;
|
||||
|
||||
@Schema(description = "Le pays du membre.", example = "FR")
|
||||
@FormParam("country")
|
||||
private String country;
|
||||
|
||||
@Schema(description = "La date de naissance du membre.")
|
||||
@FormParam("birth_date")
|
||||
private Date birth_date;
|
||||
private Date birth_date = null;
|
||||
|
||||
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com")
|
||||
@FormParam("email")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE")
|
||||
@FormParam("role")
|
||||
private RoleAsso role;
|
||||
|
||||
@Schema(description = "Le grade d'arbitrage du membre.", example = "ASSESSEUR")
|
||||
@FormParam("grade_arbitrage")
|
||||
private GradeArbitrage grade_arbitrage;
|
||||
|
||||
@Schema(description = "La photo du membre.")
|
||||
@FormParam("photo_data")
|
||||
@PartType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
private byte[] photo_data = new byte[0];
|
||||
|
||||
@ -3,22 +3,28 @@ package fr.titionfire.ffsaf.rest.from;
|
||||
import jakarta.ws.rs.FormParam;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class LicenceForm {
|
||||
@FormParam("id")
|
||||
@Schema(description = "L'identifiant de la licence. (-1 si nouvelle demande de licence)", required = true)
|
||||
private long id;
|
||||
|
||||
@FormParam("membre")
|
||||
@Schema(description = "L'identifiant du membre.", example = "1", required = true)
|
||||
private long membre;
|
||||
|
||||
@FormParam("saison")
|
||||
@Schema(description = "La saison de la licence.", example = "2025", required = true)
|
||||
private int saison;
|
||||
|
||||
@FormParam("certificate")
|
||||
private boolean certificate;
|
||||
@Schema(description = "Nom du médecin sur certificat médical.", example = "M. Jean", required = true)
|
||||
private String certificate = null;
|
||||
|
||||
@FormParam("validate")
|
||||
@Schema(description = "Licence validée (seuls les admin pourrons enregistrer cette valeur)", example = "true", required = true)
|
||||
private boolean validate;
|
||||
}
|
||||
|
||||
@ -3,19 +3,24 @@ package fr.titionfire.ffsaf.rest.from;
|
||||
import jakarta.ws.rs.FormParam;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class MemberPermForm {
|
||||
@Schema(description = "Indique si le membre est un administrateur de la fédération.", example = "false", required = true)
|
||||
@FormParam("federation_admin")
|
||||
private boolean federation_admin;
|
||||
|
||||
@Schema(description = "Indique si le membre est un utilisateur SAFCA.", example = "false", required = true)
|
||||
@FormParam("safca_user")
|
||||
private boolean safca_user;
|
||||
|
||||
@Schema(description = "Indique si le membre peut créer des compétitions sur SAFCA.", example = "false", required = true)
|
||||
@FormParam("safca_create_compet")
|
||||
private boolean safca_create_compet;
|
||||
|
||||
@Schema(description = "Indique si le membre est un super administrateur SAFCA.", example = "false", required = true)
|
||||
@FormParam("safca_super_admin")
|
||||
private boolean safca_super_admin;
|
||||
}
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
package fr.titionfire.ffsaf.rest.from;
|
||||
|
||||
import jakarta.ws.rs.FormParam;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
@ToString
|
||||
@Getter
|
||||
public class PartClubForm {
|
||||
@FormParam("id")
|
||||
@Schema(description = "Identifiant du club", example = "1", required = true)
|
||||
private String id = null;
|
||||
|
||||
@FormParam("contact")
|
||||
@Schema(description = "Les contacts du club", example = "{\"SITE\": \"www.test.com\", \"COURRIEL\": \"test@test.com\"}", required = true)
|
||||
private String contact = null;
|
||||
|
||||
@FormParam("training_location")
|
||||
@Schema(description = "Liste des lieux d'entraînement", example = "[{\"text\":\"addr 1\",\"lng\":2.24654,\"lat\":52.4868658},{\"text\":\"addr 2\",\"lng\":2.88654,\"lat\":52.7865456}]", required = true)
|
||||
private String training_location = null;
|
||||
|
||||
@FormParam("training_day_time")
|
||||
@Schema(description = "Liste des jours et horaires d'entraînement (jours 0-6, 0=>lundi) (temps en minute depuis 00:00, 122=>2h02)", example = "[{\"day\":0,\"time_start\":164,\"time_end\":240},{\"day\":3,\"time_start\":124,\"time_end\":250}]", required = true)
|
||||
private String training_day_time = null;
|
||||
|
||||
@FormParam("contact_intern")
|
||||
@Schema(description = "Contact interne du club", example = "john.doe@test.com")
|
||||
private String contact_intern = null;
|
||||
|
||||
@FormParam("address")
|
||||
@Schema(description = "Adresse postale du club", example = "1 rue de l'exemple, 75000 Paris", required = true)
|
||||
private String address = null;
|
||||
}
|
||||
@ -1,7 +1,10 @@
|
||||
package fr.titionfire.ffsaf.utils;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@RegisterForReflection
|
||||
public enum Categorie {
|
||||
SUPER_MINI,
|
||||
MINI_POUSSIN,
|
||||
@ -30,4 +33,20 @@ public enum Categorie {
|
||||
case VETERAN2 -> BUNDLE.getString("Cat.VETERAN2");
|
||||
};
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return switch (this){
|
||||
case SUPER_MINI -> "Super Mini";
|
||||
case MINI_POUSSIN -> "Mini Poussin";
|
||||
case POUSSIN -> "Poussin";
|
||||
case BENJAMIN -> "Benjamin";
|
||||
case MINIME -> "Minime";
|
||||
case CADET -> "Cadet";
|
||||
case JUNIOR -> "Junior";
|
||||
case SENIOR1 -> "Senior 1";
|
||||
case SENIOR2 -> "Senior 2";
|
||||
case VETERAN1 -> "Vétéran 1";
|
||||
case VETERAN2 -> "Vétéran 2";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
package fr.titionfire.ffsaf.utils;
|
||||
|
||||
public enum CompetitionSystem {
|
||||
SAFCA,
|
||||
}
|
||||
@ -2,6 +2,9 @@ package fr.titionfire.ffsaf.utils;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
|
||||
import javax.naming.ldap.HasControls;
|
||||
import java.util.HashMap;
|
||||
|
||||
@RegisterForReflection
|
||||
public enum Contact {
|
||||
COURRIEL("Courriel"),
|
||||
@ -20,8 +23,11 @@ public enum Contact {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
public static HashMap<String, String> toSite() {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
for (Contact contact : Contact.values()) {
|
||||
map.put(contact.toString(), contact.name);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,21 @@
|
||||
package fr.titionfire.ffsaf.utils;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
|
||||
@RegisterForReflection
|
||||
public enum Genre {
|
||||
H, F, NA
|
||||
H("Homme"),
|
||||
F("Femme"),
|
||||
NA("Non définie");
|
||||
|
||||
public final String str;
|
||||
|
||||
Genre(String name) {
|
||||
this.str = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
package fr.titionfire.ffsaf.utils;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
|
||||
@RegisterForReflection
|
||||
public enum GradeArbitrage {
|
||||
NA("N/A"),
|
||||
ASSESSEUR("Assesseur"),
|
||||
ARBITRE("Arbitre");
|
||||
|
||||
public final String name;
|
||||
public final String str;
|
||||
|
||||
GradeArbitrage(String name) {
|
||||
this.name = name;
|
||||
this.str = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
package fr.titionfire.ffsaf.utils;
|
||||
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
|
||||
public class GroupeUtils {
|
||||
public static boolean isInClubGroup(long id, JsonWebToken accessToken) {
|
||||
if (accessToken.getClaim("user_groups") instanceof Iterable<?>) {
|
||||
for (Object str : (Iterable<?>) accessToken.getClaim("user_groups")) {
|
||||
if (str.toString().substring(1, str.toString().length() - 1).startsWith("/club/" + id + "-"))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean contains(String string, JsonWebToken accessToken) {
|
||||
if (accessToken.getClaim("user_groups") instanceof Iterable<?>) {
|
||||
for (Object str : (Iterable<?>) accessToken.getClaim("user_groups")) {
|
||||
if (str.toString().substring(1, str.toString().length() - 1).contains(string))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.utils;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.Data;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -9,9 +10,13 @@ import java.util.List;
|
||||
@Data
|
||||
@RegisterForReflection
|
||||
public class PageResult<T> {
|
||||
@Schema(description = "Le numéro de la page courante.", example = "1")
|
||||
private int page;
|
||||
@Schema(description = "Le nombre d'éléments par page.", example = "10")
|
||||
private int page_size;
|
||||
@Schema(description = "Le nombre total de pages.", example = "5")
|
||||
private int page_count;
|
||||
@Schema(description = "Le nombre total d'éléments.", example = "47")
|
||||
private long result_count;
|
||||
private List<T> result = new ArrayList<>();
|
||||
}
|
||||
|
||||
@ -5,20 +5,24 @@ import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
@RegisterForReflection
|
||||
public enum RoleAsso {
|
||||
MEMBRE("Membre", 0),
|
||||
PRESIDENT("Président", 3),
|
||||
TRESORIER("Trésorier", 1),
|
||||
SECRETAIRE("Secrétaire", 2);
|
||||
PRESIDENT("Président", 7),
|
||||
VPRESIDENT("Vise-Président", 6),
|
||||
SECRETAIRE("Secrétaire", 5),
|
||||
VSECRETAIRE("Vise-Secrétaire", 4),
|
||||
TRESORIER("Trésorier", 3),
|
||||
VTRESORIER("Vise-Trésorier", 2),
|
||||
MEMBREBUREAU("Membre bureau", 1);
|
||||
|
||||
public final String name;
|
||||
public final String str;
|
||||
public final int level;
|
||||
|
||||
RoleAsso(String name, int level) {
|
||||
this.name = name;
|
||||
this.str = name;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
22
src/main/java/fr/titionfire/ffsaf/utils/ScoreEmbeddable.java
Normal file
22
src/main/java/fr/titionfire/ffsaf/utils/ScoreEmbeddable.java
Normal file
@ -0,0 +1,22 @@
|
||||
package fr.titionfire.ffsaf.utils;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RegisterForReflection
|
||||
|
||||
@Embeddable
|
||||
public class ScoreEmbeddable {
|
||||
int n_round;
|
||||
int s1;
|
||||
int s2;
|
||||
}
|
||||
56
src/main/java/fr/titionfire/ffsaf/utils/SecurityCtx.java
Normal file
56
src/main/java/fr/titionfire/ffsaf/utils/SecurityCtx.java
Normal file
@ -0,0 +1,56 @@
|
||||
package fr.titionfire.ffsaf.utils;
|
||||
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
import jakarta.enterprise.context.RequestScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@RequestScoped
|
||||
public class SecurityCtx {
|
||||
@Inject
|
||||
JsonWebToken idToken;
|
||||
|
||||
@Inject
|
||||
SecurityIdentity securityIdentity;
|
||||
|
||||
public Set<String> getRoles() {
|
||||
return securityIdentity.getRoles();
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
if (idToken == null)
|
||||
return null;
|
||||
return idToken.getSubject();
|
||||
}
|
||||
|
||||
public boolean roleHas(String role) {
|
||||
if (role == null)
|
||||
return false;
|
||||
return securityIdentity.getRoles().contains(role);
|
||||
}
|
||||
|
||||
public boolean isInClubGroup(long id) {
|
||||
if (idToken == null || idToken.getClaim("user_groups") == null)
|
||||
return false;
|
||||
|
||||
if (idToken.getClaim("user_groups") instanceof Iterable<?>) {
|
||||
for (Object str : (Iterable<?>) idToken.getClaim("user_groups")) {
|
||||
if (str.toString().substring(1, str.toString().length() - 1).startsWith("/club/" + id + "-"))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean contains(String string) {
|
||||
if (idToken.getClaim("user_groups") instanceof Iterable<?>) {
|
||||
for (Object str : (Iterable<?>) idToken.getClaim("user_groups")) {
|
||||
if (str.toString().substring(1, str.toString().length() - 1).contains(string))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package fr.titionfire.ffsaf.utils;
|
||||
|
||||
public enum SequenceType {
|
||||
Licence, Affiliation
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package fr.titionfire.ffsaf.utils;
|
||||
|
||||
public class StringSimilarity {
|
||||
|
||||
public static int similarity(String s1, String s2) {
|
||||
String longer = s1, shorter = s2;
|
||||
if (s1.length() < s2.length()) {
|
||||
longer = s2;
|
||||
shorter = s1;
|
||||
}
|
||||
int longerLength = longer.length();
|
||||
if (longerLength == 0) {
|
||||
return 1;
|
||||
}
|
||||
return editDistance(longer, shorter);
|
||||
|
||||
}
|
||||
|
||||
public static int editDistance(String s1, String s2) {
|
||||
s1 = s1.toLowerCase();
|
||||
s2 = s2.toLowerCase();
|
||||
|
||||
int[] costs = new int[s2.length() + 1];
|
||||
for (int i = 0; i <= s1.length(); i++) {
|
||||
int lastValue = i;
|
||||
for (int j = 0; j <= s2.length(); j++) {
|
||||
if (i == 0)
|
||||
costs[j] = j;
|
||||
else {
|
||||
if (j > 0) {
|
||||
int newValue = costs[j - 1];
|
||||
if (s1.charAt(i - 1) != s2.charAt(j - 1))
|
||||
newValue = Math.min(Math.min(newValue, lastValue),
|
||||
costs[j]) + 1;
|
||||
costs[j - 1] = lastValue;
|
||||
lastValue = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i > 0)
|
||||
costs[s2.length()] = lastValue;
|
||||
}
|
||||
return costs[s2.length()];
|
||||
}
|
||||
|
||||
public static void printSimilarity(String s, String t) {
|
||||
System.out.printf(
|
||||
"%d is the similarity between \"%s\" and \"%s\"%n", similarity(s, t), s, t);
|
||||
}
|
||||
|
||||
/*public static void main(String[] args) {
|
||||
printSimilarity("Xavier Login", "Xavier Lojin");
|
||||
printSimilarity("Xavier Login", "Xavier ogin");
|
||||
printSimilarity("Xavier Login", "avier Login");
|
||||
printSimilarity("Xavier Login", "xavier login");
|
||||
printSimilarity("Xavier Login", "Xaviér Login");
|
||||
printSimilarity("Xavier Gomme Login", "Xavier Login");
|
||||
printSimilarity("Xavier Login", "Xavier Gomme Login");
|
||||
printSimilarity("Xavier Login", "Xavier");
|
||||
printSimilarity("Xavier Login", "Login");
|
||||
printSimilarity("Jule", "Julles");
|
||||
printSimilarity("Xavier", "Xaviér");
|
||||
printSimilarity("Xavier", "xavvie");
|
||||
}*/
|
||||
}
|
||||
@ -1,8 +1,18 @@
|
||||
package fr.titionfire.ffsaf.utils;
|
||||
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jodd.net.MimeTypes;
|
||||
import net.sf.jmimemagic.Magic;
|
||||
import net.sf.jmimemagic.MagicException;
|
||||
import net.sf.jmimemagic.MagicMatchNotFoundException;
|
||||
import net.sf.jmimemagic.MagicParseException;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Calendar;
|
||||
@ -11,6 +21,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class Utils {
|
||||
private static final org.jboss.logging.Logger LOGGER = Logger.getLogger(Utils.class);
|
||||
|
||||
public static int getSaison() {
|
||||
return getSaison(new Date());
|
||||
@ -27,20 +38,63 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static Uni<String> moveMedia(long idSrc, long idDest, String media, String dirSrc, String dirDst) {
|
||||
return Uni.createFrom().nullItem().map(__ -> {
|
||||
File dirFile = new File(media, dirSrc);
|
||||
if (!dirFile.exists())
|
||||
return "Not found";
|
||||
|
||||
File dirDestFile = new File(media, dirDst);
|
||||
if (!dirDestFile.exists())
|
||||
if (!dirDestFile.mkdirs())
|
||||
return "Fail to create directory " + dirDestFile;
|
||||
|
||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(idSrc + ".");
|
||||
File[] files = dirFile.listFiles(filter);
|
||||
if (files == null || files.length == 0)
|
||||
return "Not found";
|
||||
|
||||
FilenameFilter filter2 = (directory, filename) -> filename.startsWith(idDest + ".");
|
||||
File[] files2 = dirDestFile.listFiles(filter2);
|
||||
if (files2 != null) {
|
||||
for (File file : files2) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.renameTo(new File(dirDestFile,
|
||||
file.getName().replaceFirst(String.valueOf(idSrc), String.valueOf(idDest))));
|
||||
}
|
||||
|
||||
return "Ok";
|
||||
});
|
||||
}
|
||||
|
||||
public static Future<String> replacePhoto(long id, byte[] input, String media, String dir) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
if (input == null || input.length == 0)
|
||||
return "OK";
|
||||
|
||||
try (InputStream is = new BufferedInputStream(new ByteArrayInputStream(input))) {
|
||||
String mimeType = URLConnection.guessContentTypeFromStream(is);
|
||||
String mimeType;
|
||||
try {
|
||||
mimeType = Magic.getMagicMatch(input, false).getMimeType();
|
||||
} catch (MagicParseException | MagicMatchNotFoundException | MagicException e) {
|
||||
mimeType = URLConnection.guessContentTypeFromStream(is);
|
||||
}
|
||||
String[] detectedExtensions = MimeTypes.findExtensionsByMimeTypes(mimeType, false);
|
||||
if (detectedExtensions.length == 0)
|
||||
throw new IOException("Fail to detect file extension for MIME type " + mimeType);
|
||||
|
||||
File dirFile = new File(media, dir);
|
||||
if (!dirFile.exists())
|
||||
if (dirFile.mkdirs())
|
||||
if (!dirFile.mkdirs())
|
||||
throw new IOException("Fail to create directory " + dir);
|
||||
|
||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id));
|
||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(id + ".");
|
||||
File[] files = dirFile.listFiles(filter);
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
@ -57,4 +111,85 @@ public class Utils {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Uni<Response> getMediaFile(long id, String media, String dirname,
|
||||
Uni<?> uniBase) throws URISyntaxException {
|
||||
return getMediaFile(id, media, dirname, null, uniBase);
|
||||
}
|
||||
|
||||
public static Uni<Response> getMediaFile(long id, String media, String dirname, String out_filename,
|
||||
Uni<?> uniBase) throws URISyntaxException {
|
||||
Future<Pair<File, byte[]>> future = CompletableFuture.supplyAsync(() -> {
|
||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(id + ".");
|
||||
File[] files = new File(media, dirname).listFiles(filter);
|
||||
if (files != null && files.length > 0) {
|
||||
File file = files[0];
|
||||
try {
|
||||
byte[] data = Files.readAllBytes(file.toPath());
|
||||
return new Pair<>(file, data);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
Future<byte[]> future2 = CompletableFuture.supplyAsync(() -> {
|
||||
try (InputStream st = Utils.class.getClassLoader().getResourceAsStream("asset/blank-profile-picture.png")) {
|
||||
if (st == null)
|
||||
return null;
|
||||
return st.readAllBytes();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return uniBase.chain(__ -> Uni.createFrom().future(future)
|
||||
.chain(filePair -> {
|
||||
if (filePair == null) {
|
||||
return Uni.createFrom().future(future2).map(data -> {
|
||||
if (data == null)
|
||||
return Response.noContent().build();
|
||||
|
||||
String mimeType = "image/apng";
|
||||
Response.ResponseBuilder resp = Response.ok(data);
|
||||
resp.type(MediaType.APPLICATION_OCTET_STREAM);
|
||||
resp.header(HttpHeaders.CONTENT_LENGTH, data.length);
|
||||
resp.header(HttpHeaders.CONTENT_TYPE, mimeType);
|
||||
resp.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"inline; " + ((out_filename == null) ? "" : "filename=\"" + out_filename + "\""));
|
||||
return resp.build();
|
||||
});
|
||||
} else {
|
||||
return Uni.createFrom().item(() -> {
|
||||
String mimeType = URLConnection.guessContentTypeFromName(filePair.getKey().getName());
|
||||
|
||||
Response.ResponseBuilder resp = Response.ok(filePair.getValue());
|
||||
resp.type(MediaType.APPLICATION_OCTET_STREAM);
|
||||
resp.header(HttpHeaders.CONTENT_LENGTH, filePair.getValue().length);
|
||||
resp.header(HttpHeaders.CONTENT_TYPE, mimeType);
|
||||
resp.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"inline; " + ((out_filename == null) ? "" : "filename=\"" + out_filename + "\""));
|
||||
return resp.build();
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static Uni<?> deleteMedia(long id, String media, String dir) {
|
||||
return Uni.createFrom().nullItem().map(__ -> {
|
||||
File dirFile = new File(media, dir);
|
||||
if (!dirFile.exists())
|
||||
return "OK";
|
||||
|
||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(id + ".");
|
||||
File[] files = dirFile.listFiles(filter);
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
return "Ok";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,165 +0,0 @@
|
||||
package fr.titionfire.ffsaf.ws;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.websocket.*;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ServerEndpoint("/api/ws/file/{code}")
|
||||
@ApplicationScoped
|
||||
public class FileSocket {
|
||||
private static final Logger logger = Logger.getLogger(FileSocket.class);
|
||||
public static Map<String, FileRecv> sessions = new ConcurrentHashMap<>();
|
||||
|
||||
@ConfigProperty(name = "upload_dir")
|
||||
String media;
|
||||
|
||||
/*@Scheduled(every = "10s")
|
||||
void increment() {
|
||||
sessions.forEach((key, value) -> {
|
||||
if (System.currentTimeMillis() - value.time > 60000) {
|
||||
closeAndDelete(value);
|
||||
if (value.session != null && value.session.isOpen()) {
|
||||
try {
|
||||
value.session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "Timeout"));
|
||||
} catch (IOException e) {
|
||||
StringWriter errors = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(errors));
|
||||
logger.error(errors.toString());
|
||||
}
|
||||
}
|
||||
sessions.remove(key);
|
||||
}
|
||||
});
|
||||
}*/
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam("code") String code) {
|
||||
try {
|
||||
if (sessions.containsKey(code)) {
|
||||
FileRecv fileRecv = sessions.get(code);
|
||||
fileRecv.session = session;
|
||||
fileRecv.file = new File(media + "-ext", "record/" + fileRecv.name);
|
||||
fileRecv.fos = new FileOutputStream(fileRecv.file, false);
|
||||
logger.info("Start reception of file: " + fileRecv.file.getAbsolutePath());
|
||||
} else {
|
||||
session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "File not found"));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
StringWriter errors = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(errors));
|
||||
logger.error(errors.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose(Session session, @PathParam("code") String code) {
|
||||
if (sessions.containsKey(code)) {
|
||||
FileRecv fileRecv = sessions.get(code);
|
||||
if (fileRecv.fos != null) {
|
||||
try {
|
||||
fileRecv.fos.close();
|
||||
} catch (IOException e) {
|
||||
StringWriter errors = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(errors));
|
||||
logger.error(errors.toString());
|
||||
}
|
||||
}
|
||||
logger.info("File received: " + fileRecv.file.getAbsolutePath());
|
||||
sessions.remove(code);
|
||||
}
|
||||
}
|
||||
|
||||
@OnError
|
||||
public void onError(Session session, @PathParam("code") String code, Throwable throwable) {
|
||||
if (sessions.containsKey(code)) {
|
||||
closeAndDelete(sessions.get(code));
|
||||
sessions.remove(code);
|
||||
}
|
||||
logger.error("Error on file reception: " + throwable.getMessage());
|
||||
}
|
||||
|
||||
|
||||
@OnMessage
|
||||
public void onMessage(String message, @PathParam("code") String code) {
|
||||
if (message.equals("cancel")) {
|
||||
if (sessions.containsKey(code)) {
|
||||
closeAndDelete(sessions.get(code));
|
||||
sessions.remove(code);
|
||||
}
|
||||
logger.error("Error file " + code + " are cancel by the client");
|
||||
}
|
||||
}
|
||||
|
||||
private void closeAndDelete(FileRecv fileRecv) {
|
||||
if (fileRecv.fos != null) {
|
||||
try {
|
||||
fileRecv.fos.close();
|
||||
} catch (IOException e) {
|
||||
StringWriter errors = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(errors));
|
||||
logger.error(errors.toString());
|
||||
}
|
||||
}
|
||||
if (fileRecv.file.exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
fileRecv.file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@OnMessage
|
||||
public void onMessage(byte[] data, @PathParam("code") String code) {
|
||||
int length = (data[1] << 7) | data[2];
|
||||
|
||||
byte check_sum = 0;
|
||||
for (int j = 3; j < length + 3; j++) {
|
||||
check_sum = (byte) (check_sum ^ data[j]);
|
||||
}
|
||||
// System.out.println(length + " - " + data[1] + " - " + data[0] + " - " + check_sum);
|
||||
|
||||
if (sessions.containsKey(code)) {
|
||||
FileRecv fileRecv = sessions.get(code);
|
||||
|
||||
if (check_sum != data[0]) {
|
||||
fileRecv.session.getAsyncRemote().sendText("Error: Checksum error", result -> {
|
||||
if (result.getException() != null) {
|
||||
logger.error("Unable to send message: " + result.getException());
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
fileRecv.fos.write(data, 3, length);
|
||||
} catch (IOException e) {
|
||||
StringWriter errors = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(errors));
|
||||
logger.error(errors.toString());
|
||||
}
|
||||
|
||||
fileRecv.session.getAsyncRemote().sendText("ok", result -> {
|
||||
if (result.getException() != null) {
|
||||
logger.error("Unable to send message: " + result.getException());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@RegisterForReflection
|
||||
public static class FileRecv {
|
||||
Session session;
|
||||
String name;
|
||||
File file;
|
||||
FileOutputStream fos;
|
||||
long time;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user