1 Commits

Author SHA1 Message Date
c553ff14da start theme implementation 2024-02-03 16:24:12 +01:00
839 changed files with 2736 additions and 38586 deletions

View File

@@ -1,5 +1,5 @@
postgres-data/
ffsaf-media/
docker-compose.yml
ffsaf_cle_prive.jks
prod.env
*
!target/*-runner
!target/*-runner.jar
!target/lib/*
!target/quarkus-app/*

View File

@@ -1,118 +0,0 @@
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: Cache Maven packages
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Cache node modules
id: cache-npm
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'graalvm'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: src/main/webapp/package-lock.json
- name: Build frontend
run: |
echo "${{ vars.VITE_ENV }}" > src/main/webapp/.env
cd src/main/webapp
npm install
npm run build
cd ../../..
- name: Inject frontend in backend
run: |
rm -rf src/main/resources/META-INF/resources
mkdir -p src/main/resources/META-INF/
mv src/main/webapp/dist src/main/resources/META-INF/resources
- name: Build backend make_pdf tool
run: |
chmod 740 mvnw
cd src/main/pdf_gen
../../../mvnw clean compile assembly:single
cd ../../..
- name: Build backend
run: |
chmod 740 mvnw
./mvnw package -Pnative -DskipTests
- name: Copy runner to vps via scp
uses: appleboy/scp-action@v0.1.7 # Latest in date when creating the workflow
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.SSH_USER }}
port: ${{ secrets.SSH_PORT }}
key: ${{ secrets.SSH_KEY }}
source: "target/*-runner,src/main/resources/cacerts,src/main/docker/Dockerfile.native,docker-compose.yml,.dockerignore,src/main/pdf_gen/target/pdf_gen-*.jar"
target: ${{ secrets.TARGET_DIR }} # Need to create it first on the VPS
- name: Re-start ffsaf container
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.SSH_USER }}
port: ${{ secrets.SSH_PORT }}
key: ${{ secrets.SSH_KEY }}
script: |
cd ${{ secrets.TARGET_DIR }}
docker logs ffsaf > "log/ffsaf_logs_$(date +"%Y-%m-%d_%H-%M-%S").log" 2>&1
docker stop ffsaf
docker rm ffsaf
docker compose up --build -d ffsaf
- name: Check ffsaf container
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.SSH_USER }}
port: ${{ secrets.SSH_PORT }}
key: ${{ secrets.SSH_KEY }}
script: |
if docker ps | grep ffsaf; then
echo 'Container is running'
else
echo 'Container is not running'
exit 1 # This mark the pipeline as failed
fi

View File

@@ -59,7 +59,6 @@ jobs:
username: ${{ secrets.SSH_USER }}
port: ${{ secrets.SSH_PORT }}
key: ${{ secrets.SSH_KEY }}
command_timeout: 20m
script: |
cd ${{ secrets.TARGET_DIR }}/ffsaf
cp ../vite.env src/main/webapp/.env

4
.gitignore vendored
View File

@@ -45,9 +45,5 @@ nb-configuration.xml
# Custom
/config/application.properties
/cle_prive.jks
/mail-truststore.p12
/src/main/resources/META-INF/resources/
/media/
/media-ext/
/sign.jpg
/.gitea/workflows/test.yml

View File

@@ -1,22 +1,8 @@
# FFSAF - Intranet
# ffsaf-site
## Introduction et context
This project uses Quarkus, the Supersonic Subatomic Java Framework.
Lintranet de la Fédération française de Soft Armored Fighting a pour but centralise la prise de licences des adhérents
et laffiliation des clubs. Il permet aussi de recueillir les résultats des compétitions organisées par les clubs et,
plus récemment, den faciliter lorganisation grâce à un outil intégré.
Le système de prise de licence se divise en trois parties :
* Un formulaire public pour la première demande daffiliation ;
* Un espace club, accessible après validation, pour la saisie des informations relatives aux demandes de licence des
adhérents ;
* Un espace fédération pour accepter les demandes de licences et daffiliation.
Un espace membre permet enfin à chaque adhérent de télécharger son attestation de licence.
Pour les compétitions, le système permet la création de catégories, de poules et de matchs, avec génération automatique
des matchs au sein dune poule. Il supporte plusieurs lices, assure la synchronisation en temps réel des modifications
entre toutes les instances de lapplication web et publie automatiquement les résultats sur le site de lorganisateur.
If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ .
## Running the application in dev mode
@@ -66,3 +52,34 @@ Or, if you don't have GraalVM installed, you can run the native executable build
You can then execute your native executable with: `./target/ffsaf-site-1.0-SNAPSHOT-runner`
If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.
## Related Guides
- Reactive MySQL client ([guide](https://quarkus.io/guides/reactive-sql-clients)): Connect to the MySQL database using the reactive pattern
- RESTEasy Reactive ([guide](https://quarkus.io/guides/resteasy-reactive)): A Jakarta REST implementation utilizing build time processing and Vert.x.
This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it.
- Hibernate ORM with Panache ([guide](https://quarkus.io/guides/hibernate-orm-panache)): Simplify your persistence code for Hibernate ORM via the
active record or the repository pattern
- Reactive PostgreSQL client ([guide](https://quarkus.io/guides/reactive-sql-clients)): Connect to the PostgreSQL database using the reactive pattern
## Provided Code
### Hibernate ORM
Create your first JPA entity
[Related guide section...](https://quarkus.io/guides/hibernate-orm)
[Related Hibernate with Panache section...](https://quarkus.io/guides/hibernate-orm-panache)
### RESTEasy Reactive
Easily start your Reactive RESTful Web Services
[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources)
### RESTEasy Reactive Qute
Create your web page using Quarkus RESTEasy Reactive & Qute
[Related guide section...](https://quarkus.io/guides/qute#type-safe-templates)

View File

@@ -1,56 +0,0 @@
services:
ffsaf:
container_name: ffsaf
hostname: ffsaf
restart: always
build:
context: .
dockerfile: src/main/docker/Dockerfile.native
volumes:
- ${PWD}/ffsaf.properties:/work/config/application.properties
- ${PWD}/ffsaf_cle_prive.jks:/work/cle_prive.jks
- ${PWD}/mail-truststore.p12:/work/mail-truststore.p12
- ${PWD}/sign.jpg:/work/sign.jpg
- ${PWD}/ffsaf-media:/work/media
depends_on:
ffsaf-db:
condition: service_healthy
restart: true
networks:
- default
- intra
- nginx
healthcheck:
test: [ "CMD", "curl", "-f", "https://intra.ffsaf.fr/api" ]
interval: 30s
timeout: 5s
retries: 3
ffsaf-db:
image: public.ecr.aws/docker/library/postgres:17.2
hostname: ffsaf-db
container_name: ffsaf-db
user: postgres
restart: always
networks:
- pgadmin
- default
healthcheck:
test: [ "CMD-SHELL", "pg_isready" ]
interval: 5s
timeout: 3s
retries: 10
volumes:
- ${PWD}/postgres-data:/var/lib/postgresql/data
env_file: prod.env
networks:
intra:
name: intra
driver: bridge
pgadmin:
name: pgadmin
external: true
nginx:
name: ${NETWORK_NAME:-gateway}
external: true

83
pom.xml
View File

@@ -1,19 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>fr.titionfire</groupId>
<artifactId>ffsaf-site</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<compiler-plugin.version>3.12.1</compiler-plugin.version>
<compiler-plugin.version>3.11.0</compiler-plugin.version>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.30.5</quarkus.platform.version>
<quarkus.platform.version>3.6.5</quarkus.platform.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.2.3</surefire-plugin.version>
<surefire-plugin.version>3.1.2</surefire-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
@@ -33,52 +34,49 @@
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-qute</artifactId>
<artifactId>quarkus-resteasy-reactive-qute</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-mysql-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-jackson</artifactId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-mssql-client</artifactId>
<version>4.4.1</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.antivirus</groupId>
<artifactId>quarkus-antivirus</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-keycloak-authorization</artifactId>
@@ -86,13 +84,14 @@
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-keycloak-admin-rest-client</artifactId>
<artifactId>quarkus-keycloak-admin-client-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.42</version>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
@@ -101,39 +100,6 @@
<artifactId>jodd-util</artifactId>
<version>6.2.1</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets</artifactId>
</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>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>2.11</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mailer</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets-next</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
@@ -202,11 +168,10 @@
</activation>
<properties>
<skipITs>false</skipITs>
<quarkus.package.type>native</quarkus.package.type>
<quarkus.native.additional-build-args>
--initialize-at-run-time=com.fasterxml.jackson.databind.ext.DOMDeserializer
-H:+UnlockExperimentalVMOptions
</quarkus.native.additional-build-args>
<quarkus.native.enabled>true</quarkus.native.enabled>
<quarkus.package.jar.enabled>false</quarkus.package.jar.enabled>
</properties>
</profile>
</profiles>

View File

@@ -14,17 +14,13 @@
# docker run -i --rm -p 8080:8080 quarkus/ffsaf-site
#
###
# replace FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9 for jvm need sub application (ie. make_pdf)
FROM registry.access.redhat.com/ubi8/openjdk-17:1.18
USER 0
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9
WORKDIR /work/
RUN chown 1001 /work \
&& chmod "g+rwX" /work \
&& chown 1001:root /work
COPY --chown=1001:root target/*-runner /work/application
COPY --chown=1001:root src/main/resources/cacerts /work/cacerts
COPY --chown=1001:root src/main/pdf_gen/target/pdf_gen-*.jar /work/make_pdf.jar
COPY --chown=1001:root ffsaf/target/*-runner /work/application
COPY --chown=1001:root ffsaf/src/main/resources/cacerts /work/cacerts
RUN mkdir /work/media && chown -R 1001:root /work/media
EXPOSE 8080

View File

@@ -2,14 +2,12 @@ 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;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.jboss.resteasy.reactive.NoCache;
@Path("/hello")
@@ -32,9 +30,6 @@ public class ExampleResource {
@IdToken
JsonWebToken idToken;
@Inject
UserInfo userInfo;
/**
* Injection point for the Access Token issued by the OpenID Connect Provider
*/
@@ -59,14 +54,12 @@ public class ExampleResource {
@GET
@Produces("text/html")
@NoCache
@Operation(hidden = true)
public String getTokens() {
StringBuilder response = new StringBuilder().append("<html>")
.append("<body>")
.append("<ul>");
System.out.println(idToken);
System.out.println(accessToken);
Object userName = this.idToken.getClaim("preferred_username");
if (userName != null) {
@@ -76,17 +69,20 @@ 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.getClaim("user_groups").toString()).append("</li>");*/
}
if (scopes != null) {
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();

View File

@@ -1,27 +0,0 @@
package fr.titionfire;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
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();
}
}

View File

@@ -1,16 +1,17 @@
package fr.titionfire;
import io.quarkus.qute.Template;
import io.smallrye.mutiny.Uni;
import io.quarkus.qute.TemplateInstance;
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 org.eclipse.microprofile.openapi.annotations.Operation;
import static java.util.Objects.requireNonNull;
@Path("api/some-page")
@Path("/some-page")
public class SomePage {
private final Template page;
@@ -21,12 +22,8 @@ public class SomePage {
@GET
@Produces(MediaType.TEXT_HTML)
@Operation(hidden = true)
public Uni<String> get() {
return Uni.createFrom()
.completionStage(() -> page
.data("name", "test")
.renderAsync());
public TemplateInstance get(@QueryParam("name") String name) {
return page.data("name", name);
}
}

View File

@@ -1,68 +0,0 @@
package fr.titionfire.ffsaf;
import io.vertx.core.http.HttpServerRequest;
import jakarta.annotation.Priority;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ResourceInfo;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import org.jboss.logging.Logger;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.Scanner;
@Provider
@Priority(Priorities.USER)
public class FrontendForwardingFilterREST implements ExceptionMapper<NotFoundException> {
private static final Logger LOG = Logger.getLogger(FrontendForwardingFilterREST.class);
@Context
UriInfo info;
@Context
HttpServerRequest request;
@Context
ResourceInfo resourceInfo;
private static String text = null;
private static final String API_NAMESPACE_REGEX = "^/(api/.*|api|openapi|q/.*)";
private static final String FILENAME_REGEX = "^/.*\\.[^.]+$";
@Override
public Response toResponse(NotFoundException exception) {
final String path = info.getPath();
final String address = request.remoteAddress().toString();
boolean isApiNamespace = path.matches(API_NAMESPACE_REGEX);
if (isApiNamespace) {
LOG.infof("Request %s from IP %s => %d", "method", path, address, exception.getResponse().getStatus());
return exception.getResponse();
}
boolean isFilename = path.matches(FILENAME_REGEX);
if (isFilename) {
LOG.infof("Request %s from IP %s => %d", "method", path, address, exception.getResponse().getStatus());
return exception.getResponse();
}
boolean actualErrorResponse = resourceInfo != null && resourceInfo.getResourceMethod() != null;
if (actualErrorResponse) {
LOG.infof("Request %s from IP %s => %d", "method", path, address, exception.getResponse().getStatus());
return exception.getResponse();
}
if (text == null)
text = new Scanner(
Objects.requireNonNull(this.getClass().getResourceAsStream("/META-INF/resources/index.html")),
StandardCharsets.UTF_8).useDelimiter("\\A").next();
LOG.infof("Request %s from IP %s => redirect", "method", path, address);
return Response.status(200).entity(text).type(MediaType.TEXT_HTML_TYPE).build();
}
}

View File

@@ -1,48 +0,0 @@
package fr.titionfire.ffsaf;
import fr.titionfire.ffsaf.domain.service.TradService;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.PreMatching;
import jakarta.ws.rs.ext.Provider;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
@Provider
@PreMatching
public class UserInfoProvider implements ContainerRequestFilter {
private static final List<Locale> SUPPORTED_LANGUAGES = Arrays.asList(
Locale.FRENCH,
Locale.ENGLISH
);
@Override
public void filter(ContainerRequestContext requestContext) {
List<Locale> acceptableLanguages = requestContext.getAcceptableLanguages();
Locale selectedLocale = findFirstSupportedLanguage(acceptableLanguages);
if (selectedLocale == null)
selectedLocale = TradService.fallbackLocale;
requestContext.setProperty("userLocale", selectedLocale);
}
private Locale findFirstSupportedLanguage(List<Locale> acceptableLanguages) {
for (Locale acceptableLanguage : acceptableLanguages) {
// Vérifie si la langue est dans la liste des langues supportées
if (SUPPORTED_LANGUAGES.contains(acceptableLanguage)) {
return acceptableLanguage;
}
// Vérifie aussi par tag de langue (ex: "fr-FR" -> "fr")
String languageTag = acceptableLanguage.getLanguage();
for (Locale supportedLanguage : SUPPORTED_LANGUAGES) {
if (supportedLanguage.getLanguage().equals(languageTag)) {
return supportedLanguage;
}
}
}
return null;
}
}

View File

@@ -1,35 +0,0 @@
package fr.titionfire.ffsaf.data;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.produce.function.FunctionParameterType;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
public class CustomPostgreSQLDialect extends PostgreSQLDialect {
private static final Logger LOGGER = Logger.getLogger(PostgreSQLDialect.class);
public CustomPostgreSQLDialect() {
super();
}
@Override
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
super.initializeFunctionRegistry(functionContributions);
LOGGER.info("Initializing custom function registry");
SqmFunctionRegistry functionRegistry = functionContributions.getFunctionRegistry();
TypeConfiguration typeConfiguration = functionContributions.getTypeConfiguration();
BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
BasicType<String> stringType = basicTypeRegistry.resolve(StandardBasicTypes.STRING);
functionRegistry.namedDescriptorBuilder("unaccent").setInvariantType(stringType).setExactArgumentCount(1)
.setParameterTypes(new FunctionParameterType[]{FunctionParameterType.STRING}).register();
}
}

View File

@@ -1,17 +0,0 @@
package fr.titionfire.ffsaf.data.id;
import jakarta.persistence.Embeddable;
import lombok.*;
import java.io.Serializable;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@Embeddable
public class RegisterId implements Serializable {
private Long competitionId;
private Long membreId;
}

View File

@@ -1,29 +0,0 @@
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
@Builder
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "affiliation")
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;
}

View File

@@ -1,47 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.RoleAsso;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.*;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "affiliation_request")
public class AffiliationRequestModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
String state_id;
String address;
String contact;
String m1_lname;
String m1_fname;
String m1_email;
int m1_lincence;
RoleAsso m1_role;
String m2_lname;
String m2_fname;
String m2_email;
int m2_lincence;
RoleAsso m2_role;
String m3_lname;
String m3_fname;
String m3_email;
int m3_lincence;
RoleAsso m3_role;
int saison;
}

View File

@@ -1,64 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import java.util.Date;
import java.util.Objects;
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "card")
public class CardModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
Long comb;
Long match;
Long category;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "competition", referencedColumnName = "id")
CompetitionModel competition;
@JsonProperty("competition")
Long competitionId;
CardType type;
String reason;
@CreationTimestamp
Date date;
@Column(nullable = false, columnDefinition = "boolean default false")
boolean teamCard = false;
public boolean hasEffect(MatchModel match) {
return switch (this.type) {
case BLUE -> false;
case YELLOW -> Objects.equals(this.match, match.getId());
case RED -> Objects.equals(this.category, match.getCategory().getId())
|| Objects.equals(this.match, match.getId());
case BLACK -> true;
};
}
public enum CardType {
BLUE,
YELLOW,
RED,
BLACK
}
}

View File

@@ -1,89 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.*;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "category_preset")
public class CatPresetModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "competition", referencedColumnName = "id")
CompetitionModel competition;
String name = "";
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "category_preset_catconfig", joinColumns = @JoinColumn(name = "id_preset"))
List<CategorieEmbeddable> categories;
SwordType swordType = SwordType.NONE;
ShieldType shieldType = ShieldType.NONE;
/*
* 1 - 1 - Casque
* 2 - 2 - Gorgerin
* 3 - 4 - Coquille et Protection pelvienne
* 4 - 8 - Gant main(s) armée(s)
* 5 - 16 - Gant main bouclier
* 6 - 32 - Plastron
* 7 - 64 - Protection de bras armé(s)
* 8 - 128 - Protection de bras de bouclier
* 9 - 256 - Protection de jambes
* 10 - 512 - Protection de genoux
* 11 - 1024 - Protection de coudes
* 12 - 2048 - Protection dorsale
* 13 - 4096 - Protection de pieds
*/
int mandatoryProtection1 = 0;
int mandatoryProtection2 = 0;
@ManyToMany(mappedBy = "categoriesInscrites", fetch = FetchType.LAZY)
private List<RegisterModel> registers = new ArrayList<>();
@ManyToMany(mappedBy = "categoriesInscrites", fetch = FetchType.LAZY)
private List<CompetitionGuestModel> guest = new ArrayList<>();
public enum SwordType {
NONE,
ONE_HAND,
TWO_HAND,
SABER
}
public enum ShieldType {
NONE,
STANDARD,
ROUND,
TEARDROP,
BUCKLER
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Embeddable
public static class CategorieEmbeddable {
Categorie categorie;
long roundDuration;
long pauseDuration;
}
}

View File

@@ -1,57 +0,0 @@
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 = "category")
public class CategoryModel {
@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_category", referencedColumnName = "id")
List<MatchModel> matchs;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "id_category", referencedColumnName = "id")
List<TreeModel> tree;
Integer type;
String liceName = "1";
@Column(nullable = false, columnDefinition = "boolean default false")
boolean treeAreClassement = false;
@Column(nullable = false, columnDefinition = "boolean default false")
boolean fullClassement = false;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "id_preset", referencedColumnName = "id")
CatPresetModel preset;
}

View File

@@ -1,41 +0,0 @@
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;
import java.util.Date;
import java.util.List;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "checkout")
public class CheckoutModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(description = "Identifiant du checkout", example = "42")
Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "membre", referencedColumnName = "id")
MembreModel membre;
Date creationDate = new Date();
List<Long> licenseIds;
Integer checkoutId;
PaymentStatus paymentStatus;
public enum PaymentStatus {
PENDING, AUTHORIZED, REFUSED, UNKNOW, REGISTERED, REFUNDING, REFUNDED, CONTESTED
}
}

View File

@@ -1,38 +0,0 @@
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;
import org.hibernate.annotations.CreationTimestamp;
import java.util.Date;
import java.util.List;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "card_team")
public class ClubCardModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
Long competition;
String teamUuid;
String teamName;
CardModel.CardType type;
String reason;
@CreationTimestamp
Date date;
List<Long> cardIds;
}

View File

@@ -4,9 +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;
@Getter
@@ -18,63 +16,37 @@ import java.util.Map;
@Entity
@Table(name = "club")
public class ClubModel implements LoggableModel {
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;
@Column(columnDefinition = "TEXT")
@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;
@Column(columnDefinition = "TEXT")
@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;
String RNA;
@Schema(description = "Numéro SIRET ou RNA du club", example = "12345678901234")
String StateId;
String SIRET;
@Schema(description = "Numéro d'affiliation du club", example = "12345")
Long no_affiliation;
String no_affiliation;
@Schema(description = "Club international", example = "false")
boolean international;
@OneToMany(mappedBy = "club", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@Schema(description = "Liste des affiliations du club (optionnel)")
List<AffiliationModel> affiliations;
@Override
public String getObjectName() {
return this.name;
}
@Override
public LogModel.ObjectType getObjectType() {
return LogModel.ObjectType.Club;
}
}

View File

@@ -1,9 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
public interface CombModel {
Long getCombId();
String getName();
String getName(MembreModel model, ResultPrivacy privacy);
}

View File

@@ -1,115 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "competition_guest")
public class CompetitionGuestModel implements CombModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "competition", referencedColumnName = "id")
CompetitionModel competition;
String lname = "";
String fname = "";
Categorie categorie = null;
String club = null;
Genre genre = null;
String country = "fr";
Integer weight = null;
Integer weightReal = null;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@JoinTable(
name = "groupe_membre",
joinColumns = @JoinColumn(name = "groupe_id"),
inverseJoinColumns = @JoinColumn(name = "membre_id")
)
List<MembreModel> comb = new ArrayList<>();
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@JoinTable(
name = "groupe_guest",
joinColumns = @JoinColumn(name = "groupe_id"),
inverseJoinColumns = @JoinColumn(name = "guest_id")
)
List<CompetitionGuestModel> guest = new ArrayList<>();
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(
name = "categories_insc_guest",
joinColumns = @JoinColumn(name = "guest_id"),
inverseJoinColumns = @JoinColumn(name = "category_id")
)
List<CatPresetModel> categoriesInscrites = new ArrayList<>();
public CompetitionGuestModel(String s) {
this.fname = s.substring(0, s.indexOf(" "));
this.lname = s.substring(s.indexOf(" ") + 1);
}
@Override
public Long getCombId() {
return this.id * -1;
}
@Override
public String getName() {
if (this.isTeam())
return this.fname;
return this.fname + " " + this.lname;
}
@Override
public String getName(MembreModel model, ResultPrivacy privacy) {
return getName();
}
public boolean isTeam() {
return "__team".equals(this.lname);
}
public boolean isInTeam(Object comb_) {
if (!this.isTeam())
return false;
if (comb_ instanceof Long id_) {
if (id_ >= 0)
return comb.stream().anyMatch(membre -> Objects.equals(membre.getId(), id_));
else
return guest.stream().anyMatch(guestModel -> Objects.equals(guestModel.getId(), -id_));
}
return Stream.concat(comb.stream(), guest.stream()).anyMatch(c -> Objects.equals(c, comb_));
}
public Integer getWeight2() {
return (this.weightReal != null) ? this.weightReal : this.weight;
}
}

View File

@@ -1,86 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.RegisterMode;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "compet")
public class CompetitionModel {
@Id
@Access(AccessType.PROPERTY)
@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;
Date todate;
@Column(columnDefinition = "TEXT")
String description;
String adresse;
Date startRegister;
Date endRegister;
RegisterMode registerMode;
boolean publicVisible;
@OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
List<RegisterModel> insc;
@OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
List<CompetitionGuestModel> guests = new ArrayList<>();
@OneToMany(mappedBy = "competition", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
List<CatPresetModel> catPreset = new ArrayList<>();
List<Categorie> requiredWeight = new ArrayList<>();
List<Long> banMembre = new ArrayList<>();
String owner;
List<String> admin = new ArrayList<>();
@Column(name = "table_")
List<String> table = new ArrayList<>();
@Column(columnDefinition = "TEXT")
String data1;
@Column(columnDefinition = "TEXT")
String data2;
@Column(columnDefinition = "TEXT")
String data3;
@Column(columnDefinition = "TEXT")
String data4;
@Column(columnDefinition = "TEXT")
String config;
}

View File

@@ -1,41 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.data.id.RegisterId;
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 = "helloasso_register")
public class HelloAssoRegisterModel {
@EmbeddedId
RegisterId id;
@MapsId("competitionId")
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "id_competition")
CompetitionModel competition;
@MapsId("membreId")
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "id_membre")
MembreModel membre;
Integer orderId;
public HelloAssoRegisterModel(CompetitionModel competition, MembreModel membre, Integer orderId) {
this.id = new RegisterId(competition.getId(), membre.getId());
this.competition = competition;
this.membre = membre;
this.orderId = orderId;
}
}

View File

@@ -1,52 +0,0 @@
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
@Builder
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "licence")
public class LicenceModel implements LoggableModel {
@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;
Long club_id;
@Schema(description = "La saison de la licence.", example = "2025")
int saison;
@Schema(description = "Nom et date du médecin sur certificat médical.", example = "M. Jean¤2025-02-03", format = "<Nom>¤<yyyy-mm-dd>")
String certificate;
@Schema(description = "Licence validée", example = "true")
boolean validate;
@Schema(description = "Licence payer", example = "true")
@Column(nullable = false, columnDefinition = "boolean default false")
boolean pay = false;
@Override
public String getObjectName() {
return "licence " + id.toString();
}
@Override
public LogModel.ObjectType getObjectType() {
return LogModel.ObjectType.Licence;
}
}

View File

@@ -1,47 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.*;
import java.util.Date;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "log")
public class LogModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String subject;
Date dateTime;
ActionType action;
ObjectType object;
Long target_id;
@Column(columnDefinition = "TEXT")
String target_name;
@Column(columnDefinition = "TEXT")
String message;
public enum ActionType {
ADD, REMOVE, UPDATE
}
public enum ObjectType {
Membre, Affiliation, Licence, Club, Competition, Register, Selection
}
}

View File

@@ -1,7 +0,0 @@
package fr.titionfire.ffsaf.data.model;
public interface LoggableModel {
Long getId();
String getObjectName();
LogModel.ObjectType getObjectType();
}

View File

@@ -1,153 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@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;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "c1_guest", referencedColumnName = "id")
CompetitionGuestModel c1_guest = null;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "c2", referencedColumnName = "id")
MembreModel c2_id = null;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "c2_guest", referencedColumnName = "id")
CompetitionGuestModel c2_guest = null;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "id_category", referencedColumnName = "id")
CategoryModel category = null;
long category_ord = 0;
boolean isEnd = true;
Date date = null;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "score", joinColumns = @JoinColumn(name = "id_match"))
List<ScoreEmbeddable> scores = new ArrayList<>();
char poule = 'A';
public String getC1Name(MembreModel model, ResultPrivacy privacy) {
if (c1_id != null)
return c1_id.getName(model, privacy);
if (c1_guest != null)
return c1_guest.getName(model, privacy);
return "";
}
public String getC2Name(MembreModel model, ResultPrivacy privacy) {
if (c2_id != null)
return c2_id.getName(model, privacy);
if (c2_guest != null)
return c2_guest.getName(model, privacy);
return "";
}
public String getC1Name() {
if (c1_id != null)
return c1_id.getName();
if (c1_guest != null)
return c1_guest.getName();
return "";
}
public String getC2Name() {
if (c2_id != null)
return c2_id.getName();
if (c2_guest != null)
return c2_guest.getName();
return "";
}
public int win() {
int sum = 0;
for (ScoreEmbeddable score : this.getScores()) {
if (score.getS1() == -1000 || score.getS2() == -1000)
continue;
if (score.getS1() > score.getS2())
sum++;
else if (score.getS1() < score.getS2())
sum--;
}
return sum;
}
public CombModel getC1() {
if (this.c1_id != null)
return this.c1_id;
if (this.c1_guest != null)
return this.c1_guest;
return null;
}
public CombModel getC2() {
if (this.c2_id != null)
return this.c2_id;
if (this.c2_guest != null)
return this.c2_guest;
return null;
}
public boolean isC1(Object comb) {
if (this.c1_guest != null && this.c1_guest.isInTeam(comb))
return true;
if (comb instanceof Long id_) {
if (id_ >= 0)
return Objects.equals(this.c1_id != null ? this.c1_id.getId() : null, id_);
else
return Objects.equals(this.c1_guest != null ? this.c1_guest.getId() : null, -id_);
}
return Objects.equals(this.c1_id, comb) || Objects.equals(this.c1_guest, comb);
}
public boolean isC2(Object comb) {
if (this.c2_guest != null && this.c2_guest.isInTeam(comb))
return true;
if (comb instanceof Long id_) {
if (id_ >= 0)
return Objects.equals(this.c2_id != null ? this.c2_id.getId() : null, id_);
else
return Objects.equals(this.c2_guest != null ? this.c2_guest.getId() : null, -id_);
}
return Objects.equals(this.c2_id, comb) || Objects.equals(this.c2_guest, comb);
}
}

View File

@@ -1,14 +1,14 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.*;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.GradeArbitrage;
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;
import java.util.Objects;
@Getter
@Setter
@@ -19,109 +19,36 @@ import java.util.Objects;
@Entity
@Table(name = "membre")
public class MembreModel implements LoggableModel, CombModel {
public class MembreModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Access(AccessType.PROPERTY)
@Schema(description = "L'identifiant du membre.", examples = "1")
Long id;
@Schema(description = "L'identifiant long du membre (userID).", examples = "e81d1d35-d897-421e-8086-6c5e74d13c6e")
String userId;
@Schema(description = "Le nom du membre.", examples = "Dupont")
String lname;
@Schema(description = "Le prénom du membre.", examples = "Jean")
String fname;
@Schema(description = "La catégorie du membre.", examples = "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.", examples = "H")
Genre genre;
@Schema(description = "Le numéro de licence du membre.", examples = "12345")
Integer licence;
int licence;
@Schema(description = "Le pays du membre.", examples = "FR")
String country;
@Schema(description = "La date de naissance du membre.")
Date birth_date;
@Schema(description = "L'adresse e-mail du membre.", examples = "jean.dupont@examples.com")
String email;
@Schema(description = "Le rôle du membre dans l'association.", examples = "MEMBRE")
RoleAsso role;
@Schema(description = "Le grade d'arbitrage du membre.", examples = "NA")
GradeArbitrage grade_arbitrage;
@Schema(description = "La confidentialité de ces résultats", examples = "PUBLIC")
@Column(nullable = false, columnDefinition = "smallint default 0")
ResultPrivacy resultPrivacy = ResultPrivacy.PUBLIC;
@OneToMany(mappedBy = "membre", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@Schema(description = "Les licences du membre. (optionnel)")
List<LicenceModel> licences;
@OneToMany(mappedBy = "membre", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@Schema(description = "Les séléctions du membre. (optionnel)")
List<SelectionModel> selections;
@Override
public String getObjectName() {
return fname + " " + lname;
}
@Override
public LogModel.ObjectType getObjectType() {
return LogModel.ObjectType.Membre;
}
@Override
public String toString() {
return "MembreModel{" +
"id=" + id +
", userId='" + userId + '\'' +
", lname='" + lname + '\'' +
", fname='" + fname + '\'' +
", categorie=" + categorie +
", genre=" + genre +
", licence=" + licence +
", country='" + country + '\'' +
", birth_date=" + birth_date +
", email='" + email + '\'' +
", role=" + role +
", grade_arbitrage=" + grade_arbitrage +
'}';
}
@Override
public Long getCombId() {
return this.id;
}
@Override
public String getName() {
return this.fname + " " + this.lname;
}
@Override
public String getName(MembreModel model, ResultPrivacy privacy) {
if (model == null || !Objects.equals(this.getId(), model.getId())) {
if (model == null && this.getResultPrivacy() != ResultPrivacy.PUBLIC)
return "Anonyme";
if (this.getResultPrivacy().ordinal() > privacy.ordinal())
return "Anonyme";
}
return getName();
}
String url_photo;
}

View File

@@ -1,99 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.data.id.RegisterId;
import fr.titionfire.ffsaf.utils.Categorie;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "register")
public class RegisterModel {
@EmbeddedId
RegisterId id;
@MapsId("competitionId")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_competition")
CompetitionModel competition;
@MapsId("membreId")
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "id_membre")
MembreModel membre;
Integer weight;
Integer weightReal;
int overCategory = 0;
Categorie categorie;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "club")
@OnDelete(action = OnDeleteAction.SET_NULL)
ClubModel club = null;
@Column(nullable = false, columnDefinition = "boolean default false")
boolean lockEdit = false;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinTable(
name = "categories_insc_comb",
joinColumns = {
@JoinColumn(name = "id_competition", referencedColumnName = "id_competition"),
@JoinColumn(name = "id_membre", referencedColumnName = "id_membre")
},
inverseJoinColumns = @JoinColumn(name = "category_id")
)
List<CatPresetModel> categoriesInscrites = new ArrayList<>();
public RegisterModel(CompetitionModel competition, MembreModel membre, Integer weight, int overCategory,
Categorie categorie, ClubModel club) {
this.id = new RegisterId(competition.getId(), membre.getId());
this.competition = competition;
this.membre = membre;
this.weight = weight;
this.overCategory = overCategory;
this.categorie = categorie;
this.club = club;
}
public String getName() {
return membre.fname + " " + membre.lname;
}
public ClubModel getClub2() {
if (club == null)
return membre.club;
return club;
}
public Categorie getCategorie2() {
Categorie tmp = this.categorie;
if (tmp == null)
tmp = membre.getCategorie();
if (tmp == null)
return null;
return Categorie.values()[Math.min(tmp.ordinal() + this.overCategory, Categorie.values().length - 1)];
}
public Integer getWeight2() {
if (weightReal != null)
return weightReal;
return weight;
}
}

View File

@@ -1,44 +0,0 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.*;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "selection")
public class SelectionModel implements LoggableModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(description = "L'identifiant de la séléction.")
Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "membre", referencedColumnName = "id")
@Schema(description = "Le membre de la séléction. (optionnel)")
MembreModel membre;
@Schema(description = "La saison de la séléction.", examples = "2025")
int saison;
@Schema(description = "Catégorie de la séléction.")
Categorie categorie;
@Override
public String getObjectName() {
return "selection " + id.toString();
}
@Override
public LogModel.ObjectType getObjectType() {
return LogModel.ObjectType.Selection;
}
}

View File

@@ -1,24 +0,0 @@
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;
}

View File

@@ -1,106 +0,0 @@
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;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
@Entity
@Table(name = "tree")
public class TreeModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
@Column(name = "id_category")
Long category;
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;
public TreeModel(Long category, Integer level, MatchModel match) {
this.category = category;
this.level = level;
this.match = match;
this.left = null;
this.right = null;
}
public List<TreeModel> flat() {
List<TreeModel> out = new ArrayList<>();
this.flat(out);
return out;
}
private void flat(List<TreeModel> out) {
out.add(this);
if (this.right != null)
this.right.flat(out);
if (this.left != null)
this.left.flat(out);
}
public int death() {
int dg = 0;
int dd = 0;
if (this.right != null)
dg = this.right.death();
if (this.left != null)
dg = this.left.death();
return 1 + Math.max(dg, dd);
}
public int getMaxChildrenAtDepth(int death, int current) {
if (current == death)
return 1;
int tmp = 0;
if (this.right != null)
tmp += this.right.getMaxChildrenAtDepth(death, current + 1);
if (this.left != null)
tmp += this.left.getMaxChildrenAtDepth(death, current + 1);
return tmp;
}
public void getChildrenAtDepth (int death, int current, List<TreeModel> out) {
if (current == death) {
out.add(this);
return;
}
if (this.right != null)
this.right.getChildrenAtDepth(death, current + 1, out);
if (this.left != null)
this.left.getChildrenAtDepth(death, current + 1, out);
}
}

View File

@@ -1,9 +0,0 @@
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> {
}

View File

@@ -1,9 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.AffiliationRequestModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class AffiliationRequestRepository implements PanacheRepositoryBase<AffiliationRequestModel, Long> {
}

View File

@@ -1,10 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.CardModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class CardRepository implements PanacheRepositoryBase<CardModel, Long> {
}

View File

@@ -1,9 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.CatPresetModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class CatPresetRepository implements PanacheRepositoryBase<CatPresetModel, Long> {
}

View File

@@ -1,19 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.CategoryModel;
import fr.titionfire.ffsaf.utils.CompetitionSystem;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class CategoryRepository implements PanacheRepositoryBase<CategoryModel, Long> {
public Uni<CategoryModel> create(CategoryModel categoryModel) {
categoryModel.setSystem(CompetitionSystem.INTERNAL);
return Panache.withTransaction(() -> this.persist(categoryModel)
.invoke(categoryModel1 -> categoryModel1.setSystemId(categoryModel1.getId())))
.chain(this::persist);
}
}

View File

@@ -1,18 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.CheckoutModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class CheckoutRepository implements PanacheRepositoryBase<CheckoutModel, Long> {
public Uni<Long> countByLicenseId(long id) {
return getSession()
.chain(s -> s
.createNativeQuery("SELECT COUNT(*) FROM ffsaf__checkout WHERE ?1 = ANY(license_ids)", Long.class)
.setParameter(1, id)
.getSingleResult());
}
}

View File

@@ -1,10 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.ClubCardModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ClubCardRepository implements PanacheRepositoryBase<ClubCardModel, Long> {
}

View File

@@ -1,9 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class CompetitionGuestRepository implements PanacheRepositoryBase<CompetitionGuestModel, Long> {
}

View File

@@ -1,9 +0,0 @@
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> {
}

View File

@@ -1,9 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.HelloAssoRegisterModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class HelloAssoRegisterRepository implements PanacheRepositoryBase<HelloAssoRegisterModel, Long> {
}

View File

@@ -1,9 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.LicenceModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class LicenceRepository implements PanacheRepositoryBase<LicenceModel, Long> {
}

View File

@@ -1,9 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.LogModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class LogRepository implements PanacheRepositoryBase<LogModel, Long> {
}

View File

@@ -1,33 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.utils.CompetitionSystem;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.List;
@ApplicationScoped
public class MatchRepository implements PanacheRepositoryBase<MatchModel, Long> {
public Uni<MatchModel> create(MatchModel matchModel) {
matchModel.setSystem(CompetitionSystem.INTERNAL);
return Panache.withTransaction(() -> this.persistAndFlush(matchModel)
.invoke(matchModel1 -> matchModel1.setSystemId(matchModel1.getId())))
.chain(this::persist);
}
public Uni<Void> create(List<MatchModel> matchModel) {
if (matchModel.isEmpty())
return Uni.createFrom().voidItem();
matchModel.forEach(model -> model.setSystem(CompetitionSystem.INTERNAL));
return Panache.withTransaction(() -> this.persist(matchModel)
.call(__ -> this.flush())
.invoke(__ -> matchModel.forEach(model -> model.setSystemId(model.getId())))
.map(__ -> matchModel))
.chain(this::persist);
}
}

View File

@@ -1,11 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.id.RegisterId;
import fr.titionfire.ffsaf.data.model.RegisterModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class RegisterRepository implements PanacheRepositoryBase<RegisterModel, RegisterId> {
}

View File

@@ -1,9 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.SelectionModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class SelectionRepository implements PanacheRepositoryBase<SelectionModel, Long> {
}

View File

@@ -1,21 +0,0 @@
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);
}
}

View File

@@ -1,23 +0,0 @@
package fr.titionfire.ffsaf.data.repository;
import fr.titionfire.ffsaf.data.model.TreeModel;
import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase;
import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class TreeRepository implements PanacheRepositoryBase<TreeModel, Long> {
@WithTransaction
public Uni<Boolean> deleteTree(TreeModel entity) {
Uni<Boolean> uni = Uni.createFrom().item(false);
if (entity == null)
return uni;
if (entity.getLeft() != null)
uni = uni.chain(__ -> this.deleteTree(entity.getLeft()));
if (entity.getRight() != null)
uni = uni.chain(__ -> this.deleteTree(entity.getRight()));
return uni.chain(__ -> this.deleteById(entity.getId()));
}
}

View File

@@ -18,12 +18,14 @@ 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 StateId;
private Long no_affiliation;
private String RNA;
private String SIRET;
private String no_affiliation;
private boolean international;
public static ClubEntity fromModel (ClubModel model) {
@@ -36,13 +38,21 @@ 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())
.contact_intern(model.getContact_intern())
.StateId(model.getStateId())
.RNA(model.getRNA())
.SIRET(model.getSIRET())
.no_affiliation(model.getNo_affiliation())
.international(model.isInternational())
.build();
}
public ClubModel toModel () {
return new ClubModel(this.id, this.clubId, this.name, this.country, this.shieldURL, this.contact, this.training_location,
this.training_day_time, this.contact_intern, this.RNA, this.SIRET, this.no_affiliation,
this.international);
}
}

View File

@@ -1,74 +0,0 @@
package fr.titionfire.ffsaf.domain.entity;
import fr.titionfire.ffsaf.data.model.CatPresetModel;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.model.RegisterModel;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
@Data
@AllArgsConstructor
@RegisterForReflection
public class CombEntity {
private long id;
private String lname;
private String fname;
Categorie categorie;
String club_uuid;
String club_str;
Genre genre;
String country;
int overCategory;
Integer weight;
List<CombEntity> teamMembers;
List<Long> categoriesInscrites;
public static CombEntity fromModel(MembreModel model) {
if (model == null)
return null;
return new CombEntity(model.getId(), model.getLname(), model.getFname(), model.getCategorie(),
model.getClub() == null ? null : model.getClub().getClubId(),
model.getClub() == null ? "Sans club" : model.getClub().getName(), model.getGenre(), model.getCountry(),
0, null, new ArrayList<>(), new ArrayList<>());
}
public static CombEntity fromModel(CompetitionGuestModel model) {
if (model == null)
return null;
return new CombEntity(model.getId() * -1, model.getLname(), model.getFname(), model.getCategorie(), null,
model.getClub(), model.getGenre(), model.getCountry(), 0, model.getWeight(),
Stream.concat(model.getComb().stream().map(CombEntity::fromModel),
model.getGuest().stream().map(CombEntity::fromModel)).toList(),
new ArrayList<>());
}
public CombEntity addCategoriesInscrites(List<CatPresetModel> categoriesInscrites) {
if (categoriesInscrites == null)
return this;
this.categoriesInscrites = categoriesInscrites.stream().map(CatPresetModel::getId).toList();
return this;
}
public static CombEntity fromModel(RegisterModel registerModel) {
if (registerModel == null || registerModel.getMembre() == null)
return null;
MembreModel model = registerModel.getMembre();
return new CombEntity(model.getId(), model.getLname(), model.getFname(), registerModel.getCategorie(),
registerModel.getClub2() == null ? null : registerModel.getClub2().getClubId(),
registerModel.getClub2() == null ? "Sans club" : registerModel.getClub2().getName(), model.getGenre(),
model.getCountry(), registerModel.getOverCategory(), registerModel.getWeight(), new ArrayList<>(),
new ArrayList<>());
}
}

View File

@@ -1,38 +0,0 @@
package fr.titionfire.ffsaf.domain.entity;
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.Date;
import java.util.List;
@Data
@AllArgsConstructor
@RegisterForReflection
public class MatchEntity {
private long id;
private CombEntity c1;
private CombEntity c2;
private long categorie_ord = 0;
private boolean isEnd;
private long categorie;
private Date date;
private List<ScoreEmbeddable> scores;
private char poule;
public static MatchEntity fromModel(MatchModel model) {
if (model == null)
return null;
return new MatchEntity(model.getId(),
(model.getC1_id() == null) ? CombEntity.fromModel(model.getC1_guest()) : CombEntity.fromModel(
model.getC1_id()),
(model.getC2_id() == null) ? CombEntity.fromModel(model.getC2_guest()) : CombEntity.fromModel(
model.getC2_id()),
model.getCategory_ord(), model.isEnd(), model.getCategory().getId(), model.getDate(),
model.getScores(),
model.getPoule());
}
}

View File

@@ -1,184 +0,0 @@
package fr.titionfire.ffsaf.domain.entity;
import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.Getter;
import java.util.*;
@RegisterForReflection
public class MatchModelExtend {
final MatchModel match;
@Getter
boolean isEnd = false;
@Getter
List<ScoreEmbeddable> scoresToPrint = new ArrayList<>();
@Getter
List<ScoreEmbeddable> scoresToCompute = new ArrayList<>();
@Getter
int win = 0;
public MatchModelExtend(MatchModel match, List<CardModel> cards) {
this.match = match;
List<Long> combIds = extractCombIds(match);
List<CardModel> cards2 = cards.stream().filter(c -> combIds.contains(c.getComb()) && c.hasEffect(match))
.sorted(Comparator.comparing(CardModel::getType).reversed()).toList();
for (ScoreEmbeddable score : match.getScores()) {
if (score.getS1() == -1000 || score.getS2() == -1000)
continue;
this.scoresToCompute.add(virtualScore(score, cards2, false));
}
calc_win_end(cards2);
for (ScoreEmbeddable score : match.getScores()) {
if (score.getS1() == -1000 || score.getS2() == -1000)
continue;
this.scoresToPrint.add(virtualScore(score, cards2, true));
}
if (this.isEnd && this.scoresToPrint.isEmpty()) {
this.scoresToPrint.add(virtualScore(new ScoreEmbeddable(0, 0, 0), cards2, true));
}
}
private ScoreEmbeddable virtualScore(ScoreEmbeddable score, List<CardModel> cards2, boolean toPrint) {
if (cards2.size() > 1) {
if (!Objects.equals(cards2.get(0).getComb(), cards2.get(1).getComb()))
return new ScoreEmbeddable(score.getN_round(), toPrint ? -997 : 0, toPrint ? -997 : 0);
}
if (!cards2.isEmpty()) {
if (isC1(cards2.get(0).getComb()))
return new ScoreEmbeddable(score.getN_round(), toPrint ? -997 : 0, 10);
else
return new ScoreEmbeddable(score.getN_round(), 10, toPrint ? -997 : 0);
}
if (score.getS1() < -900 && score.getS2() < -900)
return new ScoreEmbeddable(score.getN_round(), toPrint ? score.getS1() : 0, toPrint ? score.getS2() : 0);
else if (score.getS1() < -900)
return new ScoreEmbeddable(score.getN_round(), toPrint ? score.getS1() : 0, 10);
else if (score.getS2() < -900)
return new ScoreEmbeddable(score.getN_round(), 10, toPrint ? score.getS2() : 0);
return new ScoreEmbeddable(score.getN_round(), score.getS1(), score.getS2());
}
private void calc_win_end(List<CardModel> cards2) {
if (cards2.size() > 1) {
if (!Objects.equals(cards2.get(0).getComb(), cards2.get(1).getComb())) {
this.win = 0;
this.isEnd = true;
return;
}
}
if (!cards2.isEmpty()) {
if (match.isC1(cards2.get(0).getComb())) {
this.win = -1;
} else if (match.isC2(cards2.get(0).getComb())) {
this.win = 1;
}
this.isEnd = true;
return;
}
for (ScoreEmbeddable score : this.scoresToCompute) {
if (score.getS1() > score.getS2())
win++;
else if (score.getS1() < score.getS2())
win--;
}
this.isEnd = match.isEnd();
}
private List<Long> extractCombIds(MatchModel match) {
List<Long> ids = new ArrayList<>();
if (match.getC1_id() != null)
ids.add(match.getC1_id().getId());
if (match.getC2_id() != null)
ids.add(match.getC2_id().getId());
if (match.getC1_guest() != null)
ids.add(match.getC1_guest().getId() * -1);
if (match.getC2_guest() != null)
ids.add(match.getC2_guest().getId() * -1);
return ids;
}
//--------------- Delegation methods to MatchModel ---------------
public Long getId() {
return match.getId();
}
public MembreModel getC1_id() {
return match.getC1_id();
}
public CompetitionGuestModel getC1_guest() {
return match.getC1_guest();
}
public MembreModel getC2_id() {
return match.getC2_id();
}
public CompetitionGuestModel getC2_guest() {
return match.getC2_guest();
}
public CategoryModel getCategory() {
return match.getCategory();
}
public long getCategory_ord() {
return match.getCategory_ord();
}
public Date getDate() {
return match.getDate();
}
public char getPoule() {
return match.getPoule();
}
public String getC1Name(MembreModel model, ResultPrivacy privacy) {
return match.getC1Name(model, privacy);
}
public String getC2Name(MembreModel model, ResultPrivacy privacy) {
return match.getC2Name(model, privacy);
}
public String getC2Name() {
return match.getC2Name();
}
public String getC1Name() {
return match.getC1Name();
}
public boolean isC1(Object comb) {
return match.isC1(comb);
}
public boolean isC2(Object comb) {
return match.isC2(comb);
}
public CombModel getC1() {
return match.getC1();
}
public CombModel getC2() {
return match.getC2();
}
}

View File

@@ -0,0 +1,64 @@
package fr.titionfire.ffsaf.domain.entity;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.GradeArbitrage;
import fr.titionfire.ffsaf.utils.RoleAsso;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.Date;
@Data
@Builder
@AllArgsConstructor
@RegisterForReflection
public class MembreEntity {
private long id;
private String lname = "";
private String fname = "";
private Categorie categorie;
private ClubEntity club;
private Genre genre;
private int licence;
private String country;
private Date birth_date;
private String email;
private RoleAsso role;
private GradeArbitrage grade_arbitrage;
private String url_photo;
public static MembreEntity fromModel(MembreModel model) {
if (model == null)
return null;
return new MembreEntityBuilder()
.id(model.getId())
.lname(model.getLname())
.fname(model.getFname())
.categorie(model.getCategorie())
.club(ClubEntity.fromModel(model.getClub()))
.genre(model.getGenre())
.licence(model.getLicence())
.country(model.getCountry())
.birth_date(model.getBirth_date())
.email(model.getEmail())
.role(model.getRole())
.grade_arbitrage(model.getGrade_arbitrage())
.url_photo(model.getUrl_photo())
.build();
}
public static String getFullName(MembreModel model) {
return model.getFname() + " " + model.getLname();
}
public String getFullName() {
return this.fname + " " + this.lname;
}
}

View File

@@ -1,71 +0,0 @@
package fr.titionfire.ffsaf.domain.entity;
import fr.titionfire.ffsaf.data.model.TreeModel;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
@RegisterForReflection
public class TreeEntity {
private Long id;
private Long categorie;
private Integer level;
private MatchEntity match;
private TreeEntity left;
private TreeEntity right;
private TreeEntity associatedNode;
public static TreeEntity fromModel(TreeModel model) {
if (model == null)
return null;
return new TreeEntity(model.getId(), model.getCategory(), model.getLevel(), MatchEntity.fromModel(model.getMatch()), fromModel(model.getLeft()),
fromModel(model.getRight()), null);
}
public TreeEntity getMatchNode(Long matchId) {
if (this.match != null && this.match.getId() == matchId) {
return this;
} else {
if (this.left != null) {
TreeEntity left = this.left.getMatchNode(matchId);
if (left != null) {
return left;
}
}
if (this.right != null) {
TreeEntity right = this.right.getMatchNode(matchId);
if (right != null) {
return right;
}
}
}
return null;
}
public static TreeEntity getParent(TreeEntity current, TreeEntity target) {
if (current == null) {
return null;
} else if (current.equals(target)) {
return null;
} else if (target.equals(current.left) || target.equals(current.right)) {
return current;
} else {
TreeEntity left = getParent(current.left, target);
if (left != null)
return left;
return getParent(current.right, target);
}
}
public static void setAssociated(TreeEntity current, TreeEntity next) {
if (current == null || next == null) {
return;
}
current.setAssociatedNode(next);
setAssociated(current.getLeft(), next.getLeft());
setAssociated(current.getRight(), next.getRight());
}
}

View File

@@ -1,492 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.rest.client.SirenService;
import fr.titionfire.ffsaf.rest.client.StateIdService;
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.Genre;
import fr.titionfire.ffsaf.utils.GradeArbitrage;
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.quarkus.mailer.Mail;
import io.quarkus.mailer.reactive.ReactiveMailer;
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.eclipse.microprofile.rest.client.inject.RestClient;
import org.hibernate.reactive.mutiny.Mutiny;
import org.jboss.logging.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
@WithSession
@ApplicationScoped
public class AffiliationService {
private static final Logger LOGGER = Logger.getLogger(AffiliationService.class);
@Inject
CombRepository combRepository;
@Inject
ClubRepository clubRepository;
@Inject
AffiliationRequestRepository repositoryRequest;
@Inject
AffiliationRepository repository;
@Inject
KeycloakService keycloakService;
@Inject
SequenceRepository sequenceRepository;
@Inject
LicenceRepository licenceRepository;
@Inject
ReactiveMailer reactiveMailer;
@Inject
LoggerService ls;
@Inject
VirusScannerService scanner;
@RestClient
StateIdService stateIdService;
@RestClient
SirenService sirenService;
@ConfigProperty(name = "upload_dir")
String media;
@ConfigProperty(name = "notif.affRequest.mail")
List<String> mails;
@Inject
TradService trad;
public Uni<List<AffiliationRequestModel>> getAllReq() {
return repositoryRequest.listAll();
}
public Uni<AffiliationRequestModel> pre_save(AffiliationRequestForm form, boolean unique) {
AffiliationRequestModel affModel = form.toModel();
int currentSaison = Utils.getSaison();
List<String> out = new ArrayList<>();
out.add(affModel.getState_id());
return Uni.createFrom().item(affModel)
.invoke(Unchecked.consumer(model -> {
if (model.getSaison() != currentSaison && model.getSaison() != currentSaison + 1) {
throw new DBadRequestException(trad.t("saison.non.valid"));
}
}))
.chain(() -> ((affModel.getState_id().charAt(0) == 'W') ? stateIdService.get_rna(
affModel.getState_id()) : sirenService.get_unite(affModel.getState_id())
.chain(stateIdService::getAssoDataFromUnit)).onItem().transform(o -> {
if (o.getRna() != null && !o.getRna().isBlank())
out.add(o.getRna());
if (o.getSiren() != null && !o.getSiren().isBlank())
out.add(o.getSiren());
if (o.getIdentite().getSiret_siege() != null && !o.getIdentite().getSiret_siege().isBlank())
out.add(o.getIdentite().getSiret_siege());
return out;
}).onFailure().recoverWithItem(out)
.chain(a -> repositoryRequest.count("state_id IN ?1 and saison = ?2",
out, affModel.getSaison()))
.onItem().invoke(Unchecked.consumer(count -> {
if (count != 0 && unique) {
throw new DBadRequestException(trad.t("demande.d.affiliation.deja.existante"));
}
}))
)
.chain(() -> clubRepository.find("StateId IN ?1", out).firstResult().chain(club ->
repository.count("club = ?1 and saison = ?2", club, affModel.getSaison())))
.onItem().invoke(Unchecked.consumer(count -> {
if (count != 0) {
throw new DBadRequestException(trad.t("affiliation.deja.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 DBadRequestException(trad.t("licence.membre.n.1.inconnue"));
}
})) : Uni.createFrom().nullItem())
)
.call(model -> ((model.getM2_lincence() != -1) ? combRepository.find("licence",
model.getM2_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) {
throw new DBadRequestException(trad.t("licence.membre.n.2.inconnue"));
}
})) : Uni.createFrom().nullItem())
)
.call(model -> ((model.getM3_lincence() != -1) ? combRepository.find("licence",
model.getM3_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) {
throw new DBadRequestException(trad.t("licence.membre.n.3.inconnue"));
}
})) : Uni.createFrom().nullItem())
);
}
public Uni<?> saveEdit(AffiliationRequestForm form) {
return pre_save(form, false)
.chain(model -> repositoryRequest.findById(form.getId())
.onItem().ifNull().failWith(new DNotFoundException(trad.t("demande.d.affiliation.non.trouve")))
.chain(origine -> {
origine.setName(model.getName());
origine.setAddress(model.getAddress());
origine.setContact(model.getContact());
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) {
LOGGER.debug("Affiliation Request Created");
LOGGER.debug(form.toString());
// noinspection ReactiveStreamsUnusedPublisher
return pre_save(form, true)
.chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model)))
.onItem()
.invoke(m -> Utils.uploadFile(scanner, form.getLogo(), m.getId(), media, "aff_request/logo"))
.invoke(m -> Utils.uploadFile(scanner, form.getStatus(), m.getId(), media, "aff_request/status"))
.call(model -> reactiveMailer.send(
Mail.withText("no-reply@ffsaf.fr",
"[NOTIF] FFSAF - Nouvelle demande d'affiliation",
String.format(
"""
Une nouvelle demande d'affiliation a été déposée sur l'intranet pour le club: %s.
""", model.getName())
).setFrom("FFSAF <no-reply@ffsaf.fr>")
.addBcc(mails.toArray(String[]::new))
))
.map(__ -> "Ok");
}
public Uni<?> saveAdmin(AffiliationRequestSaveForm form) {
LOGGER.debug("Affiliation Request Saved");
LOGGER.debug(form.toString());
return repositoryRequest.findById(form.getId())
.onItem().ifNull().failWith(new DNotFoundException(trad.t("demande.d.affiliation.non.trouve")))
.map(model -> {
model.setName(form.getName());
model.setState_id(form.getState_id());
model.setAddress(form.getAddress());
model.setContact(form.getContact());
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(m -> Utils.uploadFile(scanner, form.getLogo(), m.getId(), media, "aff_request/logo"))
.invoke(m -> Utils.uploadFile(scanner, form.getStatus(), m.getId(), 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());
membreModel.setGrade_arbitrage(GradeArbitrage.NA);
membreModel.setGenre(Genre.NA);
membreModel.setCountry("FR");
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())
.onFailure().invoke(t -> LOGGER.warnf("Failed to init account: %s", t.getMessage())).onFailure()
.recoverWithNull() :
keycloakService.setClubGroupMembre(m, club).map(__ -> m.getUserId()))
.call(userId -> keycloakService.setAutoRoleMembre(userId, m.getRole(), m.getGrade_arbitrage()))
.call(userId -> keycloakService.setEmail(userId, m.getEmail())))
.call(m -> Mutiny.fetch(m.getLicences())
.call(l1 -> l1 != null && l1.stream().anyMatch(l -> l.getSaison() == saison) ?
Uni.createFrom().nullItem() :
Panache.withTransaction(() -> licenceRepository.persist(
new LicenceModel(null, m, club.getId(), saison, null, true, false)))
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, m.getObjectName(),
licenceModel))));
}
public Uni<?> accept(AffiliationRequestSaveForm form) {
LOGGER.debug("Affiliation Request Accepted");
LOGGER.debug(form.toString());
return repositoryRequest.findById(form.getId())
.onItem().ifNull().failWith(new DNotFoundException(trad.t("demande.d.affiliation.non.trouve")))
.chain(req ->
clubRepository.find("StateId = ?1", form.getState_id()).firstResult()
.chain(model -> (model == null) ? acceptNew(form, req) : acceptOld(form, req, model))
.call(club -> setMembre(form.new Member(1), club, req.getSaison()).onFailure()
.recoverWithNull()
.call(__ -> setMembre(form.new Member(2), club, req.getSaison()).onFailure()
.recoverWithNull()
.call(___ -> setMembre(form.new Member(3), club, req.getSaison()))))
.onItem()
.invoke(model -> Utils.uploadFile(scanner, form.getLogo(), form.getId(), media,
"aff_request/logo"))
.invoke(model -> Utils.uploadFile(scanner, form.getStatus(), form.getId(), media,
"aff_request/status"))
.onItem()
.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) {
LOGGER.debug("New Club Accepted");
return Uni.createFrom().nullItem()
.chain(() -> {
ClubModel club = new ClubModel();
club.setName(form.getName());
club.setCountry("FR");
club.setStateId(form.getState_id());
club.setAddress(form.getAddress());
club.setContact_intern(form.getContact());
club.setAffiliations(new ArrayList<>());
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));
})
.call(club -> reactiveMailer.send(
Mail.withHtml(form.getM1_email(), "FFSAF - Creation de votre compte sur l'intranet",
String.format(Utils.HTML_HEADER, "FFSAF - Creation de votre compte sur l'intranet") +
String.format("""
<p>Votre demande d'affiliation pour le club <span class="highlight">%s</span> a été acceptée.</p>
<p>LLe numéro d'affiliation de votre club est le <span class="highlight">%d</span>.</p>
""",
club.getName(), club.getNo_affiliation()) +
Utils.HTML_FOOTER
)
.setText(String.format("""
Bonjour,
Votre demande d'affiliation pour le club %s a été acceptée.
Le numéro d'affiliation de votre club est le %d.
Cordialement,
L'équipe de la FFSAF
""", club.getName(), club.getNo_affiliation()))
.setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr")
.addTo(form.getM2_email(), form.getM3_email())
));
}
private Uni<ClubModel> acceptOld(AffiliationRequestSaveForm form, AffiliationRequestModel model, ClubModel club) {
AtomicBoolean nameChange = new AtomicBoolean(false);
LOGGER.debug("Old Club Accepted");
return Uni.createFrom().nullItem()
.chain(() -> {
if (!form.getName().equals(club.getName())) {
club.setName(form.getName());
nameChange.set(true);
}
club.setCountry("FR");
club.setStateId(form.getState_id());
club.setAddress(form.getAddress());
club.setContact_intern(form.getContact());
return Panache.withTransaction(() -> clubRepository.persist(club)
.chain(() -> repository.persist(new AffiliationModel(null, club, model.getSaison())))
.chain(() -> repositoryRequest.delete(model)))
.call(() -> nameChange.get() ? keycloakService.updateGroupFromClub(
club) // update group in keycloak
: Uni.createFrom().nullItem());
})
.map(__ -> club);
}
public Uni<SimpleReqAffiliation> getRequest(long id) {
return repositoryRequest.findById(id).map(SimpleReqAffiliation::fromModel)
.onItem().ifNull().failWith(new DNotFoundException(trad.t("demande.d.affiliation.non.trouve")))
.call(out -> clubRepository.find("StateId = ?1", out.getStateId()).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.getState_id(), 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(trad.t("club.non.trouve")))
.call(model -> Mutiny.fetch(model.getAffiliations()))
.chain(model -> repositoryRequest.list("state_id = ?1", model.getStateId())
.map(reqs -> reqs.stream().map(req ->
new SimpleAffiliation(req.getId() * -1, model.getStateId(), 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(trad.t("club.non.trouve")))
.call(model -> Mutiny.fetch(model.getAffiliations()))
.invoke(Unchecked.consumer(club -> {
if (club.getAffiliations().stream().anyMatch(affiliation -> affiliation.getSaison() == saison)) {
throw new DBadRequestException(trad.t("affiliation.deja.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, String reason, boolean federationAdmin) {
return repositoryRequest.findById(id)
.call(aff -> federationAdmin ? reactiveMailer.send(
Mail.withHtml(aff.getM1_email(), "FFSAF - Votre demande d'affiliation a été rejetée.",
String.format(Utils.HTML_HEADER, "FFSAF - Votre demande d'affiliation a été rejetée.") +
String.format("""
<p>Votre demande d'affiliation pour le club %s a été rejetée pour la/les raison(s) suivante(s):<br/>%s</p>
<p>Si vous rencontrez un problème ou si vous avez des questions, n'hésitez pas à nous contacter à l'adresse <a href="mailto:contact@ffsaf.fr">contact@ffsaf.fr</a>.</p>
""",
aff.getName(), reason) +
Utils.HTML_FOOTER
)
.setText(String.format("""
Bonjour,
Votre demande d'affiliation pour le club %s a été rejetée pour la/les raison(s) suivante(s):
%s
Si vous rencontrez un problème ou si vous avez des questions, n'hésitez pas à nous contacter à l'adresse contact@ffsaf.fr.
Cordialement,
L'équipe de la FFSAF
""", aff.getName(), reason))
.setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("contact@ffsaf.fr")
.addTo(aff.getM2_email(), aff.getM3_email())
) : Uni.createFrom().nullItem())
.chain(aff -> Panache.withTransaction(() -> repositoryRequest.delete(aff)))
.call(__ -> Utils.deleteMedia(id, media, "aff_request/logo"))
.call(__ -> Utils.deleteMedia(id, media, "aff_request/status"));
}
}

View File

@@ -1,227 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.CardModel;
import fr.titionfire.ffsaf.data.model.ClubCardModel;
import fr.titionfire.ffsaf.data.model.CompetitionModel;
import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.ws.recv.RCard;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.panache.common.Sort;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@WithSession
@ApplicationScoped
public class CardService {
@Inject
CardRepository cardRepository;
@Inject
ClubCardRepository clubCardRepository;
@Inject
RegisterRepository registerRepository;
@Inject
CompetitionGuestRepository competitionGuestRepository;
@Inject
MatchRepository matchRepository;
@Inject
TradService trad;
private static final List<CardModel.CardType> COMPETITION_LEVEL_CARDS = List.of(
CardModel.CardType.YELLOW,
CardModel.CardType.RED,
CardModel.CardType.BLACK
);
private List<Long> extractCombIds(MatchModel match) {
List<Long> ids = new ArrayList<>();
if (match.getC1_id() != null)
ids.add(match.getC1_id().getId());
if (match.getC2_id() != null)
ids.add(match.getC2_id().getId());
if (match.getC1_guest() != null)
ids.add(match.getC1_guest().getId() * -1);
if (match.getC2_guest() != null)
ids.add(match.getC2_guest().getId() * -1);
return ids;
}
public Uni<List<CardModel>> getForMatch(MatchModel match) {
return cardRepository.list(
"competition = ?1 AND (type IN ?2 OR (type = CardType.BLUE AND category = ?4)) AND comb IN ?3",
match.getCategory().getCompet(), COMPETITION_LEVEL_CARDS,
extractCombIds(match), match.getCategory().getId());
}
public Uni<List<CardModel>> getAll(CompetitionModel competition) {
return cardRepository.list("competition = ?1", competition);
}
public Uni<RCard.SendCardAdd> checkCanBeAdded(RCard.SendCardAdd card, MatchModel matchModel) {
return cardRepository.find("competition = ?1 AND comb = ?2",
Sort.descending("type"),
matchModel.getCategory().getCompet(), card.combId())
.firstResult()
.map(card_ -> {
if (card.type() == CardModel.CardType.BLUE) {
return card_ == null || (card_.getType() == CardModel.CardType.BLUE
&& !Objects.equals(card_.getCategory(), matchModel.getCategory().getId()));
}
if (card.type() == CardModel.CardType.BLACK) {
return card_ != null && card_.getType() == CardModel.CardType.RED;
}
return card_ == null || card_.getType().ordinal() < card.type().ordinal();
})
.chain(b -> {
if (b)
return Uni.createFrom().item(card);
else
return Uni.createFrom().failure(new DBadRequestException(trad.t("card.cannot.be.added")));
});
}
public Uni<List<CardModel>> addTeamCartToNewComb(Long combId, String teamUuid, String teamName,
CompetitionModel competition) {
return clubCardRepository.list("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3)",
Sort.ascending("type"), competition.getId(), teamUuid, teamName)
.chain(clubCards -> {
Uni<?> queue = Uni.createFrom().voidItem();
List<CardModel> addCards = new ArrayList<>();
for (ClubCardModel clubCard : clubCards) {
CardModel model = new CardModel();
model.setCompetition(competition);
model.setCompetitionId(competition.getId());
model.setComb(combId);
model.setTeamCard(true);
model.setType(clubCard.getType());
model.setDate(clubCard.getDate());
queue = queue.call(__ -> Panache.withTransaction(() -> cardRepository.persist(model))
.invoke(addCards::add)
.call(() -> {
clubCard.getCardIds().add(model.getId());
return Panache.withTransaction(() -> clubCardRepository.persist(clubCard));
}));
}
return queue.replaceWith(addCards);
});
}
public Uni<Void> rmTeamCardFromComb(Long combId, String uuid) {
return cardRepository.delete("comb = ?1 AND competition.uuid = ?2 AND teamCard = True", combId, uuid)
.replaceWithVoid();
}
public Uni<List<CardModel>> addTeamCard(CompetitionModel competition, String teamUuid, String teamName,
CardModel.CardType type, String reason) {
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3)",
Sort.descending("type"), competition.getId(), teamUuid, teamName)
.firstResult()
.map(card_ -> {
if (type == CardModel.CardType.BLACK) {
return card_ != null && card_.getType() == CardModel.CardType.RED;
}
return card_ == null || card_.getType().ordinal() < type.ordinal();
})
.chain(b -> {
if (!b)
return Uni.createFrom().failure(new DBadRequestException(trad.t("card.cannot.be.added")));
if (teamUuid != null) {
return registerRepository.list("competition = ?1 AND club.clubId = ?2", competition, teamUuid)
.map(l -> l.stream().map(r -> r.getMembre().getId()).toList());
} else {
return competitionGuestRepository.list("competition = ?1 AND club = ?2", competition,
teamName)
.map(l -> l.stream().map(r -> r.getId() * -1).toList());
}
})
.chain(combIds -> cardRepository.list("competition = ?1 AND comb IN ?2", competition, combIds)
.map(cards -> {
List<CardModel> newCards = new ArrayList<>();
for (Long id : combIds) {
Optional<CardModel> optional = cards.stream()
.filter(c -> id.equals(c.getComb()) && c.getType() == type).findAny();
CardModel model = new CardModel();
model.setCompetition(competition);
model.setCompetitionId(competition.getId());
model.setComb(id);
model.setTeamCard(true);
if (optional.isEmpty()) {
model.setType(type);
} else {
model.setType(
CardModel.CardType.values()[Math.min(optional.get().getType().ordinal() + 1,
CardModel.CardType.BLACK.ordinal())]);
}
newCards.add(model);
}
return newCards;
})
)
.call(newCards -> Panache.withTransaction(() -> cardRepository.persist(newCards)))
.call(newCards -> {
ClubCardModel model = new ClubCardModel();
model.setCompetition(competition.getId());
model.setTeamUuid(teamUuid);
model.setTeamName(teamName);
model.setType(type);
model.setReason(reason);
model.setCardIds(newCards.stream().map(CardModel::getId).toList());
return Panache.withTransaction(() -> clubCardRepository.persist(model));
});
}
public Uni<List<CardModel>> recvReturnState(CompetitionModel competition, RCard.SendTeamCardReturnState state) {
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3) AND type = ?4",
competition.getId(), state.teamUuid(), state.teamName(), state.type())
.firstResult()
.chain(o -> cardRepository.list("id IN ?1", o.getCardIds()))
.call(cards -> matchRepository.list("category.compet = ?1 AND category.id IN ?2", competition,
state.selectedCategory())
.invoke(matches -> {
for (CardModel card : cards) {
for (MatchModel m : matches.stream()
.filter(m -> extractCombIds(m).contains(card.getComb())).toList()) {
if (state.state() == 1) {
card.setCategory(m.getCategory().getId());
} else if (state.state() == 2) {
card.setCategory(m.getCategory().getId());
if (Objects.equals(m.getId(), state.selectedMatch()))
card.setMatch(m.getId());
}
}
}
})
.chain(() -> Panache.withTransaction(() -> cardRepository.persist(cards))));
}
public Uni<List<Long>> rmTeamCard(CompetitionModel competition, String teamUuid, String teamName,
CardModel.CardType type) {
return clubCardRepository.find("competition = ?1 AND (teamUuid = ?2 OR teamName = ?3) AND type = ?4",
competition.getId(), teamUuid, teamName, type)
.firstResult()
.chain(card -> Uni.createFrom().item(card.getCardIds())
.call(() -> Panache.withTransaction(() -> cardRepository.delete("id IN ?1", card.getCardIds())))
.call(() -> Panache.withTransaction(() -> clubCardRepository.delete(card))));
}
}

View File

@@ -1,279 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.rest.data.CategoryData;
import fr.titionfire.ffsaf.rest.data.CategoryFullData;
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 CategoryService {
@Inject
CategoryRepository repository;
@Inject
CompetitionRepository competRepository;
@Inject
MatchRepository matchRepository;
@Inject
TreeRepository treeRepository;
@Inject
CombRepository combRepository;
@Inject
CompetPermService permService;
@Inject
CompetitionGuestRepository competitionGuestRepository;
@Inject
TradService trad;
public Uni<CategoryData> getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
return repository.find("systemId = ?1 AND system = ?2", id, system)
.firstResult()
.onItem().ifNull().failWith(() -> new RuntimeException(trad.t("categorie.non.trouver")))
.call(data -> permService.hasAdminViewPerm(securityCtx, data.getCompet()))
.map(CategoryData::fromModel);
}
public Uni<List<CategoryData>> getAllAdmin(SecurityCtx securityCtx, CompetitionSystem system) {
return permService.getAllHaveAdminAccess(securityCtx)
.chain(ids -> repository.list("system = ?1 AND compet.id IN ?2", system, ids))
.map(pouleModels -> pouleModels.stream().map(CategoryData::fromModel).toList());
}
public Uni<CategoryData> addOrUpdate(SecurityCtx securityCtx, CompetitionSystem system, CategoryData 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(trad.t("competition.not.found")))
.call(o2 -> permService.hasEditPerm(securityCtx, o2))
.chain(competitionModel -> {
CategoryModel model = new CategoryModel();
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(CategoryData::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, CategoryModel poule,
List<MatchModel> matchModelList) {
TreeModel mm = findNode(node, data.getMatch());
if (mm == null) {
mm = new TreeModel();
mm.setId(null);
}
mm.setLevel(data.getLevel());
mm.setCategory(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<?> syncCategory(SecurityCtx securityCtx, CompetitionSystem system, CategoryFullData 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(trad.t("competition.not.found")))
.call(o -> permService.hasEditPerm(securityCtx, o))
.map(o -> {
CategoryModel model = new CategoryModel();
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.category = o;
return workData;
})
.call(o -> Panache.withTransaction(() -> repository.persist(o.category)))
.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)))
)
)
.call(o -> Mutiny.fetch(o.category.getCompet().getGuests())
.invoke(o2 -> o2.forEach(m -> o.guest.put(m.getFname() + " " + m.getLname(), m)))
.map(o2 -> data.getMatches().stream().flatMap(m -> Stream.of(m.getC1_str(), m.getC2_str())
.filter(Objects::nonNull)).distinct().filter(s -> !o.guest.containsKey(s)).map(
CompetitionGuestModel::new).toList())
.call(o3 -> o3.isEmpty() ? Uni.createFrom().nullItem() :
Uni.join().all(o3.stream().map(o4 -> competitionGuestRepository.persist(o4)).toList())
.andFailFast())
.invoke(o2 -> o2.forEach(m -> o.guest.put(m.getFname() + " " + m.getLname(), m))))
.invoke(in -> {
ArrayList<TreeModel> node = new ArrayList<>();
for (TreeModel treeModel : in.category.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.category.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.category.getMatchs(), m.getId());
if (mm == null) {
mm = new MatchModel();
mm.setId(null);
mm.setSystem(system);
mm.setSystemId(m.getId());
}
mm.setCategory(in.category);
mm.setCategory_ord(m.getCategory_ord());
mm.setC1_guest(in.guest.getOrDefault(m.getC1_str(), null));
mm.setC2_guest(in.guest.getOrDefault(m.getC2_str(), null));
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.setPoule(m.getPoule());
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.category.getTree(), in.category, in.match)).toList())
.andCollectFailures())
.map(__ -> "OK");
}
private static class WorkData {
CategoryModel category;
HashMap<Long, MembreModel> membres = new HashMap<>();
HashMap<String, CompetitionGuestModel> guest = 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(trad.t("categorie.non.trouver")))
.call(o -> permService.hasEditPerm(securityCtx, o.getCompet()))
.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 -> matchRepository.delete("category.id = ?1", o.getId()))
.chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId())));
}
}

View File

@@ -1,175 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.CheckoutModel;
import fr.titionfire.ffsaf.data.model.LogModel;
import fr.titionfire.ffsaf.data.repository.CheckoutRepository;
import fr.titionfire.ffsaf.data.repository.LicenceRepository;
import fr.titionfire.ffsaf.rest.client.HelloAssoService;
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsRequest;
import fr.titionfire.ffsaf.rest.client.dto.CheckoutIntentsResponse;
import fr.titionfire.ffsaf.rest.client.dto.CheckoutMetadata;
import fr.titionfire.ffsaf.rest.exception.DInternalError;
import fr.titionfire.ffsaf.utils.SecurityCtx;
import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.scheduler.Scheduled;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.hibernate.reactive.mutiny.Mutiny;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@WithSession
@ApplicationScoped
public class CheckoutService {
@Inject
CheckoutRepository repository;
@Inject
LicenceRepository licenceRepository;
@Inject
MembreService membreService;
@Inject
LicenceService licenceService;
@Inject
LoggerService ls;
@RestClient
HelloAssoService helloAssoService;
@ConfigProperty(name = "frontRootUrl")
String frontRootUrl;
@ConfigProperty(name = "unitLicencePrice")
int unitLicencePrice;
@ConfigProperty(name = "helloasso.organizationSlug")
String organizationSlug;
public Uni<Boolean> canDeleteLicence(long id) {
return repository.countByLicenseId(id).invoke(c -> System.out.println(c)).map(count -> count == 0);
}
public Uni<String> create(List<Long> ids, SecurityCtx securityCtx) {
return membreService.getByAccountId(securityCtx.getSubject())
.call(membreModel -> Mutiny.fetch(membreModel.getClub()))
.chain(membreModel -> {
CheckoutModel model = new CheckoutModel();
model.setMembre(membreModel);
model.setLicenseIds(ids);
model.setPaymentStatus(CheckoutModel.PaymentStatus.UNKNOW);
return Panache.withTransaction(() -> repository.persist(model));
})
.chain(checkoutModel -> {
CheckoutIntentsRequest request = new CheckoutIntentsRequest();
request.setTotalAmount(unitLicencePrice * checkoutModel.getLicenseIds().size());
request.setInitialAmount(unitLicencePrice * checkoutModel.getLicenseIds().size());
request.setItemName("%d licences %d-%d pour %s".formatted(checkoutModel.getLicenseIds().size(),
Utils.getSaison(), Utils.getSaison() + 1, checkoutModel.getMembre().getClub().getName()));
request.setBackUrl(frontRootUrl + "/club/member/pay");
request.setErrorUrl(frontRootUrl + "/club/member/pay/error");
request.setReturnUrl(frontRootUrl + "/club/member/pay/return");
request.setContainsDonation(false);
request.setPayer(new CheckoutIntentsRequest.Payer(checkoutModel.getMembre().getFname(),
checkoutModel.getMembre().getLname(), checkoutModel.getMembre().getEmail()));
request.setMetadata(new CheckoutMetadata(checkoutModel.getId()));
return helloAssoService.checkout(organizationSlug, request)
.call(response -> {
checkoutModel.setCheckoutId(response.getId());
return Panache.withTransaction(() -> repository.persist(checkoutModel));
});
})
.onFailure().transform(t -> new DInternalError(t.getMessage()))
.map(CheckoutIntentsResponse::getRedirectUrl);
}
public Uni<Response> paymentStatusChange(String state, CheckoutMetadata metadata) {
return repository.findById(metadata.getCheckoutDBId())
.chain(checkoutModel -> {
CheckoutModel.PaymentStatus newStatus = CheckoutModel.PaymentStatus.valueOf(state.toUpperCase());
Uni<?> uni = Uni.createFrom().nullItem();
if (checkoutModel.getPaymentStatus().equals(newStatus))
return uni;
if (newStatus.equals(CheckoutModel.PaymentStatus.AUTHORIZED)) {
for (Long id : checkoutModel.getLicenseIds()) {
uni = uni.chain(__ -> licenceRepository.findById(id)
.onFailure().recoverWithNull()
.call(licenceModel -> {
if (licenceModel == null) {
ls.logAnonymous(LogModel.ActionType.UPDATE, LogModel.ObjectType.Licence,
"Fail to save payment for licence (checkout n°" + checkoutModel.getCheckoutId() + ")",
"", id);
return Uni.createFrom().nullItem();
}
ls.logUpdateAnonymous("Paiement de la licence", licenceModel);
licenceModel.setPay(true);
if (licenceModel.getCertificate() != null && licenceModel.getCertificate()
.length() > 3) {
if (!licenceModel.isValidate())
ls.logUpdateAnonymous("Validation automatique de la licence",
licenceModel);
return licenceService.validateLicences(licenceModel);
} else {
return Panache.withTransaction(
() -> licenceRepository.persist(licenceModel));
}
}));
}
} else if (checkoutModel.getPaymentStatus().equals(CheckoutModel.PaymentStatus.AUTHORIZED)) {
for (Long id : checkoutModel.getLicenseIds()) {
uni = uni.chain(__ -> licenceRepository.findById(id)
.onFailure().recoverWithNull()
.call(licenceModel -> {
if (licenceModel == null)
return Uni.createFrom().nullItem();
ls.logUpdateAnonymous("Annulation automatique du paiement de la licence",
licenceModel);
licenceModel.setPay(false);
if (licenceModel.isValidate())
ls.logUpdateAnonymous(
"Annulation automatique de la validation de la licence",
licenceModel);
licenceModel.setValidate(false);
return Panache.withTransaction(() -> licenceRepository.persist(licenceModel));
}));
}
}
uni = uni.call(__ -> ls.append());
checkoutModel.setPaymentStatus(newStatus);
return uni.chain(__ -> Panache.withTransaction(() -> repository.persist(checkoutModel)));
})
.onFailure().invoke(Throwable::printStackTrace)
.map(__ -> Response.ok().build());
}
@Scheduled(cron = "0 0 * * * ?")
Uni<Void> everyHours() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR, -1);
Date dateLimit = calendar.getTime();
return repository.delete("creationDate < ?1 AND (checkoutId IS NULL OR paymentStatus = ?2)", dateLimit,
CheckoutModel.PaymentStatus.UNKNOW)
.map(__ -> null);
}
}

View File

@@ -1,42 +1,17 @@
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.AffiliationRepository;
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.*;
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.Multi;
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.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
import java.util.Collection;
import java.util.List;
@WithSession
@ApplicationScoped
@@ -45,36 +20,13 @@ public class ClubService {
@Inject
ClubRepository repository;
@Inject
AffiliationRepository affiliationRepository;
@Inject
ServerCustom serverCustom;
@Inject
CombRepository combRepository;
@Inject
KeycloakService keycloakService;
@ConfigProperty(name = "upload_dir")
String media;
@Inject
LoggerService ls;
@Inject
TradService trad;
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() {
@@ -83,281 +35,8 @@ public class ClubService {
public Uni<?> setClubId(Long id, String id1) {
return repository.findById(id).chain(clubModel -> {
ls.logChange("KC UUID", clubModel.getClubId(), id1, clubModel);
clubModel.setClubId(id1);
return Panache.withTransaction(() -> repository.persist(clubModel))
.call(() -> ls.append());
});
}
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("LOWER(name) LIKE LOWER(?1)",
Sort.ascending("name"), search).page(Page.ofSize(limit));
else
query = repository.find("LOWER(name) LIKE LOWER(?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(trad.t("club.non.trouve"));
}))
.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<List<VerySimpleMembre>> getMembers(SecurityCtx securityCtx) {
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
.invoke(Unchecked.consumer(m -> {
if (m == null || m.getClub() == null)
throw new DNotFoundException(trad.t("club.non.trouve"));
if (!securityCtx.isInClubGroup(m.getClub().getId()))
throw new DForbiddenException();
}))
.chain(m -> combRepository.list("club = ?1", m.getClub()))
.map(membreModels -> membreModels.stream()
.map(m -> new VerySimpleMembre(m.getLname(), m.getFname(), m.getLicence())).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(trad.t("club.non.trouve"));
if (!securityCtx.isInClubGroup(m.getClub().getId()))
throw new DForbiddenException();
}))
.map(MembreModel::getClub)
.call(club -> Mutiny.fetch(club.getContact()))
.chain(Unchecked.function(club -> {
ls.logChange("Contact interne", club.getContact_intern(), form.getContact_intern(), club);
club.setContact_intern(form.getContact_intern());
ls.logChange("Adresse administrative", club.getAddress(), form.getAddress(), club);
club.setAddress(form.getAddress());
try {
if (!Objects.equals(club.getContact(), MAPPER.readValue(form.getContact(), typeRef)))
ls.logUpdate("Contact(s)...", club);
club.setContact(MAPPER.readValue(form.getContact(), typeRef));
} catch (JsonProcessingException e) {
throw new DBadRequestException(trad.t("erreur.de.format.des.contacts"));
}
ls.logChange("Lieux d'entrainements", club.getTraining_location(), form.getTraining_location(),
club);
club.setTraining_location(form.getTraining_location());
ls.logChange("Horaires d'entrainements", club.getTraining_day_time(), form.getTraining_day_time(),
club);
club.setTraining_day_time(form.getTraining_day_time());
return Panache.withTransaction(() -> repository.persist(club)).call(() -> ls.append());
}))
.map(__ -> "OK");
}
public Uni<String> update(long id, FullClubForm input) {
AtomicBoolean nameChange = new AtomicBoolean(false);
return repository.findById(id).call(m -> Mutiny.fetch(m.getContact()))
.onItem().transformToUni(Unchecked.function(m -> {
TypeReference<HashMap<Contact, String>> typeRef = new TypeReference<>() {
};
if (!input.getName().equals(m.getName())) {
m.setName(input.getName());
nameChange.set(true);
}
m.setCountry(input.getCountry());
m.setInternational(input.isInternational());
if (!input.isInternational()) {
ls.logChange("Lieux d'entrainements", m.getTraining_location(), input.getTraining_location(),
m);
m.setTraining_location(input.getTraining_location());
ls.logChange("Horaires d'entrainements", m.getTraining_day_time(), input.getTraining_day_time(),
m);
m.setTraining_day_time(input.getTraining_day_time());
ls.logChange("Contact interne", m.getContact_intern(), input.getContact_intern(), m);
m.setContact_intern(input.getContact_intern());
if (input.getState_id() != null && !input.getState_id().isBlank()) {
ls.logChange("N° SIRET", m.getClubId(), input.getState_id(), m);
m.setStateId(input.getState_id());
}
ls.logChange("Adresse administrative", m.getAddress(), input.getAddress(), m);
m.setAddress(input.getAddress());
try {
if (!Objects.equals(m.getContact(), MAPPER.readValue(input.getContact(), typeRef)))
ls.logUpdate("Contact(s)...", m);
m.setContact(MAPPER.readValue(input.getContact(), typeRef));
} catch (JsonProcessingException e) {
throw new DBadRequestException(trad.t("erreur.de.format.des.contacts"));
}
}
return Panache.withTransaction(() -> repository.persist(m)).call(() -> ls.append());
}))
.call(clubModel -> nameChange.get() ? keycloakService.updateGroupFromClub(
clubModel) // update group in keycloak
: Uni.createFrom().nullItem())
.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());
if (input.getState_id() != null && !input.getState_id().isBlank())
clubModel.setStateId(input.getState_id());
clubModel.setAddress(input.getAddress());
try {
clubModel.setContact(MAPPER.readValue(input.getContact(), typeRef));
} catch (JsonProcessingException ignored) {
}
}
return Panache.withTransaction(() -> repository.persist(clubModel));
})
.call(clubModel -> ls.logAAdd(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.stream().noneMatch(m -> m.getUserId() != null) ?
Uni.createFrom().voidItem() :
Uni.join().all(list.stream().filter(m -> m.getUserId() != null)
.map(m -> keycloakService.clearUser(m.getUserId())).toList())
.andCollectFailures())
.chain(list -> list.isEmpty() ? Uni.createFrom().voidItem() :
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))
.call(clubModel -> ls.logADelete(clubModel))
.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.setState_id(clubModel.getStateId());
data.setAddress(clubModel.getAddress());
data.setContact(clubModel.getContact_intern());
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);
}
public Uni<List<ClubMapData>> getMapData() {
return affiliationRepository.list("saison >= ?1 AND club.international = ?2", Utils.getSaison() - 1, false)
.toMulti().flatMap(list -> Multi.createFrom().iterable(list))
.call(affiliationModel -> Mutiny.fetch(affiliationModel.getClub().getContact()))
.map(affiliationModel -> {
ClubModel clubModel = affiliationModel.getClub();
ClubMapData data = new ClubMapData();
data.setName(clubModel.getName());
data.setUuid(clubModel.getClubId());
if (clubModel.getTraining_location() != null) {
try {
MAPPER.readTree(clubModel.getTraining_location()).forEach(l -> {
ClubMapData.Location loc = new ClubMapData.Location();
loc.setLat(l.get("lat").asDouble());
loc.setLng(l.get("lng").asDouble());
loc.setAddr(l.get("text").asText());
data.training_location.add(loc);
});
} catch (JsonProcessingException ignored) {
}
}
data.setTraining_day_time(clubModel.getTraining_day_time());
data.setContact(clubModel.getContact());
return data;
}).collect().asList();
}
}

View File

@@ -1,318 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.CompetitionModel;
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
import fr.titionfire.ffsaf.data.repository.RegisterRepository;
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.RegisterMode;
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.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
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
CompetitionRepository competitionRepository;
@Inject
@CacheName("safca-config")
Cache cache;
@Inject
@CacheName("safca-have-access")
Cache cacheAccess;
@Inject
@CacheName("have-access")
Cache cacheNoneAccess;
@Inject
RegisterRepository registerRepository;
public Uni<SimpleCompet> getSafcaConfig(long id) {
return cache.get(id, k -> {
CompletableFuture<SimpleCompet> f = new CompletableFuture<>();
SReqCompet.getConfig(serverCustom.clients, id, f);
try {
return f.get(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new RuntimeException(e);
}
});
}
public Uni<List<Long>> getAllHaveAdminAccess(SecurityCtx securityCtx) {
ArrayList<Long> out = new ArrayList<>();
Uni<HashMap<Long, String>> safca = cacheAccess.getAsync(securityCtx.getSubject(),
k -> competitionRepository.list("system = ?1", CompetitionSystem.SAFCA)
.chain(competitionModels -> {
CompletableFuture<HashMap<String, String>> f = new CompletableFuture<>();
SReqCompet.getAllHaveAccess(serverCustom.clients, securityCtx.getSubject(), f);
return Uni.createFrom().future(f, Duration.ofMillis(500))
.onFailure().recoverWithItem(new HashMap<>())
.map(map_ -> {
HashMap<Long, String> map = new HashMap<>();
map_.forEach((key, value) -> map.put(Long.parseLong(key), value));
for (CompetitionModel model : competitionModels) {
if (model.getOwner().equals(securityCtx.getSubject()))
map.putIfAbsent(model.getId(), "owner");
else if (securityCtx.roleHas("federation_admin")
|| securityCtx.roleHas("safca_super_admin"))
map.putIfAbsent(model.getId(), "admin");
}
return map;
});
}))
.onFailure().call(throwable -> cacheAccess.invalidate(securityCtx.getSubject()));
Uni<HashMap<Long, String>> none = cacheNoneAccess.getAsync(securityCtx.getSubject(),
k -> competitionRepository.list("system = ?1", CompetitionSystem.INTERNAL)
.map(competitionModels -> {
HashMap<Long, String> map = new HashMap<>();
for (CompetitionModel model : competitionModels) {
if (model.getOwner().equals(securityCtx.getSubject()))
map.putIfAbsent(model.getId(), "owner");
else if (securityCtx.roleHas("federation_admin"))
map.putIfAbsent(model.getId(), "admin");
else if (securityCtx.isInClubGroup(
model.getClub().getId()) && (securityCtx.isClubAdmin()))
map.putIfAbsent(model.getId(), "admin");
else if (model.getAdmin().contains(securityCtx.getSubject()))
map.putIfAbsent(model.getId(), "admin");
else if (model.getTable().contains(securityCtx.getSubject()))
map.putIfAbsent(model.getId(), "table");
}
return map;
}));
return safca.invoke(map ->
map.forEach((k, v) -> {
if (v.equals("owner") || v.equals("admin"))
out.add(k);
})
)
.call(__ -> none.invoke(map ->
map.forEach((k, v) -> {
if (v.equals("owner") || v.equals("admin"))
out.add(k);
})
))
.map(__ -> out.stream().distinct().toList());
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm
*/
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
return hasViewPerm(securityCtx, Uni.createFrom().item(competitionModel));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm
*/
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, long id) {
return hasViewPerm(securityCtx, competitionRepository.findById(id));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has view perm
*/
public Uni<CompetitionModel> hasViewPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
return in.call(cm -> (cm.isPublicVisible() || cm.getRegisterMode() == RegisterMode.FREE
|| cm.getRegisterMode() == RegisterMode.HELLOASSO
|| (cm.getRegisterMode() == RegisterMode.CLUB_ADMIN && securityCtx.isClubAdmin())) ?
Uni.createFrom().nullItem() :
hasAdminViewPerm(securityCtx, cm).onFailure()
.recoverWithUni(__ ->
registerRepository.count("membre.userId = ?1 AND competition = ?2",
securityCtx.getSubject(), cm).map(Unchecked.function(c -> {
if (c == 0)
throw new DForbiddenException();
return cm;
}))
));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm
*/
public Uni<CompetitionModel> hasAdminViewPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
return hasAdminViewPerm(securityCtx, Uni.createFrom().item(competitionModel));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm
*/
public Uni<CompetitionModel> hasAdminViewPerm(SecurityCtx securityCtx, long id) {
return hasAdminViewPerm(securityCtx, competitionRepository.findById(id));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has admin view perm
*/
public Uni<CompetitionModel> hasAdminViewPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
return in.call(Unchecked.function(o -> {
if (securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin"))
return Uni.createFrom().nullItem();
if (o.getSystem() == CompetitionSystem.SAFCA)
return hasSafcaViewPerm(securityCtx, o.getId());
if (o.getAdmin().contains(securityCtx.getSubject()))
return Uni.createFrom().nullItem();
if (!securityCtx.isInClubGroup(o.getClub().getId())) // Only membre club pass here
throw new DForbiddenException();
if (o.getSystem() == CompetitionSystem.INTERNAL)
if (securityCtx.isClubAdmin())
return Uni.createFrom().nullItem();
throw new DForbiddenException();
})
);
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
*/
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
return hasEditPerm(securityCtx, Uni.createFrom().item(competitionModel));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
*/
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, long id) {
return hasEditPerm(securityCtx, competitionRepository.findById(id));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
*/
public Uni<CompetitionModel> hasEditPerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
return in.call(Unchecked.function(o -> {
if (securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin"))
return Uni.createFrom().nullItem();
if (o.getSystem() == CompetitionSystem.SAFCA)
return hasSafcaEditPerm(securityCtx, o.getId());
if (o.getSystem() == CompetitionSystem.INTERNAL) {
if (securityCtx.isInClubGroup(o.getClub().getId()) && securityCtx.isClubAdmin())
return Uni.createFrom().nullItem();
if (o.getAdmin().contains(securityCtx.getSubject()))
return Uni.createFrom().nullItem();
throw new DForbiddenException();
}
throw new DForbiddenException();
})
);
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
*/
public Uni<CompetitionModel> hasTablePerm(SecurityCtx securityCtx, CompetitionModel competitionModel) {
return hasTablePerm(securityCtx, Uni.createFrom().item(competitionModel));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
*/
public Uni<CompetitionModel> hasTablePerm(SecurityCtx securityCtx, long id) {
return hasTablePerm(securityCtx, competitionRepository.findById(id));
}
/**
* @return {@link fr.titionfire.ffsaf.data.model.CompetitionModel} if securityCtx has edit perm
*/
public Uni<CompetitionModel> hasTablePerm(SecurityCtx securityCtx, Uni<CompetitionModel> in) {
return in.call(Unchecked.function(o -> {
if (securityCtx.getSubject().equals(o.getOwner()) || securityCtx.roleHas("federation_admin"))
return Uni.createFrom().nullItem();
if (o.getSystem() == CompetitionSystem.SAFCA)
return hasSafcaTablePerm(securityCtx, o.getId());
if (o.getSystem() == CompetitionSystem.INTERNAL) {
if (securityCtx.isInClubGroup(o.getClub().getId()) && securityCtx.isClubAdmin())
return Uni.createFrom().nullItem();
if (o.getAdmin().contains(securityCtx.getSubject()))
return Uni.createFrom().nullItem();
if (o.getTable().contains(securityCtx.getSubject()))
return Uni.createFrom().nullItem();
throw new DForbiddenException();
}
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();
}));
}
private Uni<?> hasSafcaTablePerm(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();
}));
}
}

View File

@@ -1,961 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.net2.ServerCustom;
import fr.titionfire.ffsaf.net2.data.SimpleCompet;
import fr.titionfire.ffsaf.net2.request.SReqCompet;
import fr.titionfire.ffsaf.net2.request.SReqRegister;
import fr.titionfire.ffsaf.rest.client.dto.NotificationData;
import fr.titionfire.ffsaf.rest.data.*;
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.utils.*;
import fr.titionfire.ffsaf.ws.send.SRegister;
import io.quarkus.cache.Cache;
import io.quarkus.cache.CacheName;
import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.mailer.Mail;
import io.quarkus.mailer.reactive.ReactiveMailer;
import io.smallrye.mutiny.Multi;
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 jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.Response;
import org.hibernate.reactive.mutiny.Mutiny;
import org.jboss.logging.Logger;
import org.keycloak.representations.idm.UserRepresentation;
import java.util.*;
import java.util.stream.Stream;
@WithSession
@ApplicationScoped
public class CompetitionService {
private static final Logger LOGGER = Logger.getLogger(CompetitionService.class);
@Inject
CompetitionRepository repository;
@Inject
CategoryRepository categoryRepository;
@Inject
MatchRepository matchRepository;
@Inject
RegisterRepository registerRepository;
@Inject
KeycloakService keycloakService;
@Inject
CombRepository combRepository;
@Inject
CompetitionGuestRepository competitionGuestRepository;
@Inject
CatPresetRepository catPresetRepository;
@Inject
ServerCustom serverCustom;
@Inject
MembreService membreService;
@Inject
CompetPermService permService;
@Inject
HelloAssoRegisterRepository helloAssoRepository;
@SuppressWarnings("CdiInjectionPointsInspection")
@Inject
ReactiveMailer reactiveMailer;
@Inject
Vertx vertx;
@Inject
SRegister sRegister;
@Inject
@CacheName("safca-config")
Cache cache;
@Inject
@CacheName("safca-have-access")
Cache cacheAccess;
@Inject
@CacheName("have-access")
Cache cacheNoneAccess;
@Inject
TradService trad;
public Uni<CompetitionData> getById(SecurityCtx securityCtx, Long id) {
return permService.hasViewPerm(securityCtx, id).map(cm -> {
CompetitionData out = CompetitionData.fromModelLight(cm);
if (cm.getAdmin() != null) {
out.setCanEditRegisters(cm.getAdmin().stream().anyMatch(u -> u.equals(securityCtx.getSubject())));
}
return out;
});
}
public Uni<CompetitionData> getByIdAdmin(SecurityCtx securityCtx, Long id) {
if (id == 0) {
return Uni.createFrom().item(new CompetitionData());
}
return permService.hasAdminViewPerm(securityCtx, id)
.call(competitionModel -> Mutiny.fetch(competitionModel.getCatPreset()))
.chain(competitionModel -> Mutiny.fetch(competitionModel.getInsc())
.chain(insc -> Mutiny.fetch(competitionModel.getGuests())
.map(guest -> CompetitionData.fromModel(competitionModel).addInsc(insc, guest)
.addPresets(competitionModel.getCatPreset()))))
.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) {
List<CompetitionData> out = new ArrayList<>();
return permService.getAllHaveAdminAccess(securityCtx)
.call(ids -> repository.list("id IN ?1", ids)
.invoke(cm -> {
out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList());
out.forEach(competition -> competition.setCanEdit(true));
}))
.chain(ids -> repository.list("id NOT IN ?1 AND ?2 IN admin", ids, securityCtx.getSubject())
.map(cm -> {
out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList());
out.forEach(competition -> competition.setCanEditRegisters(true));
List<Long> ids2 = new ArrayList<>(ids);
ids2.addAll(cm.stream().map(CompetitionModel::getId).toList());
return ids2;
}))
.call(ids ->
repository.list("id NOT IN ?1 AND (publicVisible = TRUE OR registerMode IN ?2)", ids,
securityCtx.isClubAdmin() ? List.of(RegisterMode.FREE, RegisterMode.HELLOASSO,
RegisterMode.CLUB_ADMIN) : List.of(RegisterMode.FREE, RegisterMode.HELLOASSO))
.invoke(cm -> out.addAll(cm.stream().map(CompetitionData::fromModelLight).toList()))
.call(cm -> registerRepository.list(
"membre.userId = ?1 AND competition.id NOT IN ?2 AND competition NOT IN ?3",
securityCtx.getSubject(), ids, cm)
.chain(registerModels -> {
Uni<Void> uni = Uni.createFrom().nullItem();
for (RegisterModel registerModel : registerModels) {
uni = uni.call(__ -> Mutiny.fetch(registerModel.getCompetition())
.invoke(cm2 -> out.add(CompetitionData.fromModelLight(cm2))));
}
return uni;
})
))
.map(__ -> out);
}
public Uni<List<CompetitionData>> getAllAdmin(SecurityCtx securityCtx) {
return permService.getAllHaveAdminAccess(securityCtx)
.chain(ids -> repository.list("id IN ?1", ids))
.map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList());
}
public Uni<List<CompetitionData>> getAllSystemAdmin(SecurityCtx securityCtx,
CompetitionSystem system) {
return permService.getAllHaveAdminAccess(securityCtx)
.chain(ids -> repository.list("system = ?1 AND id IN ?2", system, ids))
.map(pouleModels -> pouleModels.stream().map(CompetitionData::fromModel).toList());
}
public Uni<List<CompetitionData>> getAllSystemTable(SecurityCtx securityCtx,
CompetitionSystem system) {
return repository.list("system = ?1", system)
.chain(l -> Uni.join().all(l.stream().map(cm ->
permService.hasTablePerm(securityCtx, cm).onFailure().recoverWithNull()
).toList()).andCollectFailures())
.map(l -> l.stream().filter(Objects::nonNull).map(CompetitionData::fromModel).toList());
}
public Uni<CompetitionData> addOrUpdate(SecurityCtx securityCtx, CompetitionData data) {
if (data.getId() == null) {
return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult()
.invoke(Unchecked.consumer(combModel -> {
if (!securityCtx.getRoles().contains("create_compet") && !securityCtx.getRoles()
.contains("federation_admin"))
throw new DForbiddenException(trad.t("vous.ne.pouvez.pas.creer.de.competition"));
}))
.map(MembreModel::getClub)
.chain(clubModel -> {
CompetitionModel model = new CompetitionModel();
model.setId(null);
model.setSystem(data.getSystem());
model.setClub(clubModel);
model.setInsc(new ArrayList<>());
model.setGuests(new ArrayList<>());
model.setUuid(UUID.randomUUID().toString());
model.setOwner(securityCtx.getSubject());
model.setCatPreset(new ArrayList<>());
copyData(data, model);
return Panache.withTransaction(() -> repository.persist(model));
})
.call(model -> syncPreset(data, model))
.map(CompetitionData::fromModel)
.call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate(
securityCtx.getSubject()) : Uni.createFrom().nullItem())
.call(c -> (c.getSystem() == CompetitionSystem.INTERNAL) ? cacheNoneAccess.invalidate(
securityCtx.getSubject()) : Uni.createFrom().nullItem());
} else {
return permService.hasEditPerm(securityCtx, data.getId())
.call(model -> Mutiny.fetch(model.getCatPreset()))
.chain(model -> {
copyData(data, model);
return vertx.getOrCreateContext().executeBlocking(() -> // Update owner
keycloakService.getUser(data.getOwner()).map(UserRepresentation::getId).orElse(null))
.invoke(Unchecked.consumer(newOwner -> {
if (newOwner == null)
throw new DBadRequestException(
String.format(trad.t("user.not.found"), data.getOwner()));
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)));
})
.call(model -> syncPreset(data, model))
.map(model -> CompetitionData.fromModel(model).addPresets(model.getCatPreset()))
.call(c -> (c.getSystem() == CompetitionSystem.SAFCA) ? cacheAccess.invalidate(
securityCtx.getSubject()) : Uni.createFrom().nullItem())
.call(c -> (c.getSystem() == CompetitionSystem.INTERNAL) ? cacheNoneAccess.invalidate(
securityCtx.getSubject()) : Uni.createFrom().nullItem());
}
}
private Uni<?> syncPreset(CompetitionData data, CompetitionModel model) {
List<Long> toRemoveId = model.getCatPreset().stream()
.map(CatPresetModel::getId)
.filter(id -> data.getPresets().stream().noneMatch(preset -> Objects.equals(preset.getId(), id)))
.toList();
for (PresetData preset : data.getPresets()) {
CatPresetModel presetModel;
if (preset.getId() != null && preset.getId() > 0) {
presetModel = model.getCatPreset().stream()
.filter(p -> p.getId().equals(preset.getId()))
.findFirst()
.orElse(new CatPresetModel());
} else {
presetModel = new CatPresetModel();
model.getCatPreset().add(presetModel);
}
presetModel.setCompetition(model);
presetModel.setName(preset.getName());
presetModel.setSwordType(preset.getSword());
presetModel.setShieldType(preset.getShield());
presetModel.setCategories(preset.getCategories());
presetModel.setMandatoryProtection1(preset.getMandatoryProtection1());
presetModel.setMandatoryProtection2(preset.getMandatoryProtection2());
}
// Remove deleted presets
model.getCatPreset().removeIf(presetModel -> toRemoveId.contains(presetModel.getId()));
return Panache.withTransaction(() -> repository.persist(model)
.call(__ -> {
if (!toRemoveId.isEmpty()) {
return catPresetRepository.delete("id IN ?1", toRemoveId);
}
return Uni.createFrom().nullItem();
}));
}
private void copyData(CompetitionData data, CompetitionModel model) {
if (model.getBanMembre() == null)
model.setBanMembre(new ArrayList<>());
model.setName(data.getName());
model.setAdresse(data.getAdresse());
model.setDescription(data.getDescription());
model.setDate(data.getDate());
model.setTodate(data.getDate());
model.setPublicVisible(data.isPublicVisible());
model.setStartRegister(data.getStartRegister());
model.setEndRegister(data.getEndRegister());
model.setRegisterMode(data.getRegisterMode());
model.setRequiredWeight(data.getRequiredWeight());
model.setData1(data.getData1());
model.setData2(data.getData2());
model.setData3(data.getData3());
model.setData4(data.getData4());
}
public Uni<List<SimpleRegisterComb>> getRegister(SecurityCtx securityCtx, Long id, String source) {
if ("admin".equals(source))
return permService.hasEditPerm(securityCtx, id)
.chain(c -> {
Uni<List<SimpleRegisterComb>> uni = Mutiny.fetch(c.getInsc())
.onItem().transformToMulti(Multi.createFrom()::iterable)
.onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences()))
.onItem().call(combModel -> Mutiny.fetch(combModel.getCategoriesInscrites()))
.map(cm -> SimpleRegisterComb.fromModel(cm, cm.getMembre().getLicences())
.setCategorieInscrite(cm.getCategoriesInscrites()))
.collect().asList();
return uni
.call(l -> Mutiny.fetch(c.getGuests())
.onItem().transformToMulti(Multi.createFrom()::iterable)
.filter(g -> !g.isTeam())
.onItem().call(guest -> Mutiny.fetch(guest.getCategoriesInscrites()))
.map(guest -> SimpleRegisterComb.fromModel(guest)
.setCategorieInscrite(guest.getCategoriesInscrites()))
.collect().asList()
.invoke(l::addAll));
});
if ("club".equals(source))
return Uni.createFrom().nullItem()
.invoke(Unchecked.consumer(__ -> {
if (!securityCtx.isClubAdmin())
throw new DForbiddenException();
}))
.chain(__ -> membreService.getByAccountId(securityCtx.getSubject()))
.chain(model -> registerRepository.list("competition.id = ?1 AND membre.club = ?2", id,
model.getClub()))
.onItem().transformToMulti(Multi.createFrom()::iterable)
.onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences()))
.onItem().call(combModel -> Mutiny.fetch(combModel.getCategoriesInscrites()))
.map(combModel -> SimpleRegisterComb.fromModel(combModel, combModel.getMembre().getLicences())
.setCategorieInscrite(combModel.getCategoriesInscrites()))
.collect().asList();
return membreService.getByAccountId(securityCtx.getSubject())
.chain(model -> registerRepository.find("competition.id = ?1 AND membre = ?2", id, model).firstResult()
.call(rm -> rm == null ? Uni.createFrom().voidItem() :
Mutiny.fetch(rm.getCategoriesInscrites()))
.map(rm -> rm == null ? List.of() : List.of(SimpleRegisterComb.fromModel(rm, List.of())
.setCategorieInscrite(rm.getCategoriesInscrites()))));
}
public Uni<SimpleRegisterComb> addRegisterComb(SecurityCtx securityCtx, Long id, RegisterRequestData data,
String source) {
if ("admin".equals(source))
if (data.getLicence() == null || data.getLicence() != -1) { // not a guest
return permService.hasEditPerm(securityCtx, id)
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
.call(combModel -> Mutiny.fetch(combModel.getLicences()))
.call(combModel -> {
if (c.getBanMembre() == null)
c.setBanMembre(new ArrayList<>());
c.getBanMembre().remove(combModel.getId());
return Panache.withTransaction(() -> repository.persist(c));
})
.chain(combModel -> updateRegister(data, c, combModel, true, false)))
.map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences())
.setCategorieInscrite(r.getCategoriesInscrites()));
} else {
return permService.hasEditPerm(securityCtx, id)
.chain(c -> competitionGuestRepository.findById(data.getId() * -1)
.map(g -> {
if (g != null)
return g;
CompetitionGuestModel model = new CompetitionGuestModel();
model.setCompetition(c);
return model;
}))
.invoke(model -> {
model.setFname(data.getFname());
if (data.getLname().equals("__team"))
model.setLname("_team");
else
model.setLname(data.getLname());
model.setGenre(data.getGenre());
model.setClub(data.getClub());
model.setCountry(data.getCountry());
model.setWeightReal(data.getWeightReal());
model.setCategorie(data.getCategorie());
if (model.getCompetition().getRequiredWeight().contains(model.getCategorie()))
model.setWeight(data.getWeight());
})
.call(g -> Mutiny.fetch(g.getCategoriesInscrites()))
.call(g -> catPresetRepository.list("competition = ?1 AND id IN ?2", g.getCompetition(),
data.getCategoriesInscrites())
.invoke(cats -> {
g.getCategoriesInscrites().clear();
g.getCategoriesInscrites().addAll(cats);
g.getCategoriesInscrites()
.removeIf(cat -> cat.getCategories().stream()
.noneMatch(e -> e.getCategorie().equals(g.getCategorie())));
}))
.chain(model -> Panache.withTransaction(() -> competitionGuestRepository.persist(model))
.call(r -> model.getCompetition().getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegister(model.getCompetition().getUuid(),
r) : Uni.createFrom().voidItem()))
.map(g -> SimpleRegisterComb.fromModel(g).setCategorieInscrite(g.getCategoriesInscrites()));
}
if ("club".equals(source))
return repository.findById(id)
.invoke(Unchecked.consumer(cm -> {
if (!(cm.getRegisterMode() == RegisterMode.CLUB_ADMIN || cm.getRegisterMode() == RegisterMode.FREE)
|| !securityCtx.isClubAdmin())
throw new DForbiddenException();
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException(trad.t("inscription.fermee"));
}))
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
.call(combModel -> Mutiny.fetch(combModel.getLicences()))
.invoke(Unchecked.consumer(model -> {
if (!securityCtx.isInClubGroup(model.getClub().getId()))
throw new DForbiddenException();
if (c.getBanMembre().contains(model.getId()))
throw new DForbiddenException(trad.t("insc.err1"));
}))
.chain(combModel -> updateRegister(data, c, combModel, false, false)))
.map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences())
.setCategorieInscrite(r.getCategoriesInscrites()));
return repository.findById(id)
.invoke(Unchecked.consumer(cm -> {
if (cm.getRegisterMode() != RegisterMode.FREE)
throw new DForbiddenException();
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException(trad.t("inscription.fermee"));
}))
.chain(c -> membreService.getByAccountId(securityCtx.getSubject())
.invoke(Unchecked.consumer(model -> {
if (c.getBanMembre().contains(model.getId()))
throw new DForbiddenException(trad.t("insc.err2"));
}))
.chain(combModel -> updateRegister(data, c, combModel, false, false)))
.map(r -> SimpleRegisterComb.fromModel(r, List.of()).setCategorieInscrite(r.getCategoriesInscrites()));
}
public Uni<List<SimpleRegisterComb>> addRegistersComb(SecurityCtx securityCtx, Long id,
List<RegisterRequestData> datas,
String source) {
if (!"admin".equals(source))
return Uni.createFrom().failure(new DForbiddenException());
return Multi.createFrom().iterable(datas).onItem().transformToUni(data ->
makeImportUpdate(securityCtx, id, data).onFailure().recoverWithItem(t -> {
SimpleRegisterComb errorComb = new SimpleRegisterComb();
errorComb.setLicence(-42);
errorComb.setFname("ERROR");
errorComb.setLname(t.getMessage());
return errorComb;
})).concatenate().collect().asList();
}
private Uni<SimpleRegisterComb> makeImportUpdate(SecurityCtx securityCtx, Long id, RegisterRequestData data) {
if (data.getLicence() == null || data.getLicence() != -1) { // not a guest
return permService.hasEditPerm(securityCtx, id)
.chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname())
.call(combModel -> Mutiny.fetch(combModel.getLicences()))
.call(combModel -> {
if (c.getBanMembre() == null)
c.setBanMembre(new ArrayList<>());
c.getBanMembre().remove(combModel.getId());
return Panache.withTransaction(() -> repository.persist(c));
})
.chain(combModel -> updateRegister(data, c, combModel, true, true)))
.map(r -> SimpleRegisterComb.fromModel(r, r.getMembre().getLicences())
.setCategorieInscrite(r.getCategoriesInscrites()));
} else {
return permService.hasEditPerm(securityCtx, id)
.chain(c -> findGuestOrInit(data.getFname(), data.getLname(), c))
.invoke(Unchecked.consumer(model -> {
if (data.getCategorie() == null)
throw new DBadRequestException(trad.t("categorie.requise"));
model.setCategorie(data.getCategorie());
if (data.getGenre() == null) {
if (model.getGenre() == null)
data.setGenre(Genre.NA);
} else
model.setGenre(data.getGenre());
if (data.getClub() == null) {
if (model.getClub() == null)
data.setClub("");
} else
model.setClub(data.getClub());
if (data.getCountry() == null) {
if (model.getCountry() == null)
data.setCountry("FR");
} else
model.setCountry(data.getCountry());
if (model.getCompetition().getRequiredWeight().contains(model.getCategorie())) {
if (data.getCountry() != null)
model.setWeight(data.getWeight());
}
}))
.call(g -> Mutiny.fetch(g.getCategoriesInscrites()))
.call(g -> catPresetRepository.list("competition = ?1 AND id IN ?2", g.getCompetition(),
data.getCategoriesInscrites())
.invoke(cats -> {
g.getCategoriesInscrites().clear();
g.getCategoriesInscrites().addAll(cats);
g.getCategoriesInscrites().removeIf(cat -> cat.getCategories().stream()
.noneMatch(e -> e.getCategorie().equals(g.getCategorie())));
}))
.chain(model -> Panache.withTransaction(() -> competitionGuestRepository.persist(model))
.call(r -> model.getCompetition().getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegister(model.getCompetition().getUuid(),
r) : Uni.createFrom().voidItem()))
.map(g -> SimpleRegisterComb.fromModel(g).setCategorieInscrite(g.getCategoriesInscrites()));
}
}
private Uni<RegisterModel> updateRegister(RegisterRequestData data, CompetitionModel c,
MembreModel combModel, boolean admin, boolean append) {
return registerRepository.find("competition = ?1 AND membre = ?2", c, combModel).firstResult()
.onFailure().recoverWithNull()
.map(Unchecked.function(r -> {
if (r != null) {
if (!admin && r.isLockEdit())
throw new DForbiddenException(trad.t("insc.err3"));
if (data.getOverCategory() != null || !append)
if (data.getOverCategory() == null)
r.setOverCategory(0);
else
r.setOverCategory(data.getOverCategory());
r.setCategorie(
(combModel.getBirth_date() == null) ? combModel.getCategorie() :
Utils.getCategoryFormBirthDate(combModel.getBirth_date(),
c.getDate()));
int days = Utils.getDaysBeforeCompetition(c.getDate());
if (days > -7)
r.setClub(combModel.getClub());
if (c.getRequiredWeight().contains(r.getCategorie2()))
if (data.getCountry() != null || !append)
r.setWeight(data.getWeight());
if (admin) {
r.setWeightReal(data.getWeightReal());
r.setLockEdit(data.isLockEdit());
}
} else {
r = new RegisterModel(c, combModel, data.getWeight(), data.getOverCategory(),
(combModel.getBirth_date() == null) ? combModel.getCategorie() :
Utils.getCategoryFormBirthDate(combModel.getBirth_date(),
c.getDate()),
(combModel.getClub() == null) ? null : combModel.getClub());
if (admin) {
r.setWeightReal(data.getWeightReal());
r.setLockEdit(data.isLockEdit());
} else
r.setLockEdit(false);
}
if (c.getSystem() == CompetitionSystem.SAFCA) {
SReqRegister.sendIfNeed(serverCustom.clients,
new CompetitionData.SimpleRegister(r.getMembre().getId(),
r.getOverCategory(), r.getWeight2(), r.getCategorie(),
(r.getClub() == null) ? null : r.getClub().getId(),
(r.getClub() == null) ? null : r.getClub().getName()), c.getId());
}
return r;
}))
.call(r -> Mutiny.fetch(r.getCategoriesInscrites()).chain(__ ->
catPresetRepository.list("competition = ?1 AND id IN ?2", c, data.getCategoriesInscrites())
.invoke(cats -> {
if (data.isQuick()) {
cats.removeIf(cat -> r.getCategoriesInscrites().stream()
.anyMatch(cp -> cp.equals(cat)));
} else {
r.getCategoriesInscrites().clear();
}
r.getCategoriesInscrites().addAll(cats);
r.getCategoriesInscrites()
.removeIf(cat -> cat.getCategories().stream()
.noneMatch(e -> e.getCategorie().equals(r.getCategorie2())));
})))
.chain(r -> Panache.withTransaction(() -> registerRepository.persist(r)))
.call(r -> c.getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegister(c.getUuid(), r) : Uni.createFrom().voidItem());
}
private Uni<CompetitionGuestModel> findGuestOrInit(String fname, String lname, CompetitionModel competition) {
if (fname == null || lname == null)
return Uni.createFrom().failure(new DBadRequestException(trad.t("nom.et.prenom.requis")));
return competitionGuestRepository.find(
"unaccent(lname) ILIKE unaccent(?1) AND unaccent(fname) ILIKE unaccent(?2) AND competition = ?3",
lname, fname, competition).firstResult()
.map(guestModel -> {
if (guestModel == null) {
CompetitionGuestModel model = new CompetitionGuestModel();
model.setFname(fname);
if (lname.equals("__team"))
model.setLname("_team");
else
model.setLname(lname);
model.setCompetition(competition);
return model;
}
return guestModel;
});
}
private Uni<MembreModel> findComb(Long licence, String fname, String lname) {
if (licence != null && licence > 0) {
return combRepository.find("licence = ?1", licence).firstResult()
.invoke(Unchecked.consumer(combModel -> {
if (combModel == null)
throw new DForbiddenException(String.format(trad.t("licence.non.trouve"), licence));
}));
} else {
if (fname == null || lname == null)
return Uni.createFrom().failure(new DBadRequestException(trad.t("nom.et.prenom.requis")));
return combRepository.find("unaccent(lname) ILIKE unaccent(?1) AND unaccent(fname) ILIKE unaccent(?2)",
lname,
fname).firstResult()
.invoke(Unchecked.consumer(combModel -> {
if (combModel == null)
throw new DForbiddenException(String.format(trad.t("combattant.non.trouve"), fname, lname));
}));
}
}
public Uni<Void> removeRegisterComb(SecurityCtx securityCtx, Long id, Long combId, String source, boolean ban) {
if ("admin".equals(source))
return permService.hasEditPerm(securityCtx, id)
.chain(cm -> {
if (cm.getBanMembre() == null)
cm.setBanMembre(new ArrayList<>());
if (ban) {
if (!cm.getBanMembre().contains(combId))
cm.getBanMembre().add(combId);
} else {
cm.getBanMembre().remove(combId);
}
return Panache.withTransaction(() -> repository.persist(cm));
})
.chain(c -> deleteRegister(combId, c, true));
if ("club".equals(source))
return repository.findById(id)
.invoke(Unchecked.consumer(cm -> {
if (!(cm.getRegisterMode() == RegisterMode.CLUB_ADMIN || cm.getRegisterMode() == RegisterMode.FREE)
|| !securityCtx.isClubAdmin())
throw new DForbiddenException();
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException(trad.t("inscription.fermee"));
}))
.call(cm -> membreService.getById(combId)
.invoke(Unchecked.consumer(model -> {
if (model == null)
throw new DNotFoundException(
String.format(trad.t("le.membre.n.existe.pas"), combId));
if (!securityCtx.isInClubGroup(model.getClub().getId()))
throw new DForbiddenException();
})))
.chain(c -> deleteRegister(combId, c, false));
return repository.findById(id)
.call(cm -> membreService.getByAccountId(securityCtx.getSubject())
.invoke(Unchecked.consumer(model -> {
if (cm.getRegisterMode() != RegisterMode.FREE || !Objects.equals(model.getId(), combId))
throw new DForbiddenException();
if (new Date().before(cm.getStartRegister()) || new Date().after(cm.getEndRegister()))
throw new DBadRequestException(trad.t("inscription.fermee"));
})))
.chain(c -> deleteRegister(combId, c, false));
}
private Uni<Void> deleteRegister(Long combId, CompetitionModel c, boolean admin) {
if (admin && combId < 0) {
return competitionGuestRepository.find("competition = ?1 AND id = ?2", c, combId * -1).firstResult()
.onFailure().transform(t -> new DBadRequestException(trad.t("combattant.non.inscrit")))
.call(Unchecked.function(
model -> Panache.withTransaction(() -> competitionGuestRepository.delete(model))
.call(r -> c.getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterRemove(c.getUuid(), combId) : Uni.createFrom()
.voidItem())))
.replaceWithVoid();
}
return registerRepository.find("competition = ?1 AND membre.id = ?2", c, combId).firstResult()
.onFailure().transform(t -> new DBadRequestException(trad.t("combattant.non.inscrit")))
.call(Unchecked.function(registerModel -> {
if (!admin && registerModel.isLockEdit())
throw new DForbiddenException(trad.t("insc.err3"));
return Panache.withTransaction(() -> registerRepository.delete(registerModel))
.call(r -> c.getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterRemove(c.getUuid(), combId) : Uni.createFrom().voidItem());
}))
.replaceWithVoid();
}
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 -> categoryRepository.list("compet = ?1", competitionModel)
.call(pouleModels -> pouleModels.isEmpty() ? Uni.createFrom().nullItem() :
Uni.join().all(pouleModels.stream()
.map(pouleModel -> Panache.withTransaction(
() -> matchRepository.delete("poule = ?1", pouleModel.getId())))
.toList())
.andCollectFailures()))
.call(competitionModel -> Panache.withTransaction(
() -> categoryRepository.delete("compet = ?1", competitionModel)))
.call(competitionModel -> Panache.withTransaction(
() -> registerRepository.delete("competition = ?1", competitionModel)))
.call(competitionModel -> Panache.withTransaction(
() -> competitionGuestRepository.delete("competition = ?1", competitionModel)))
.chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId())))
.invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id))
.call(__ -> cache.invalidate(id));
}
public Uni<SimpleCompetData> getInternalData(SecurityCtx securityCtx, Long id) {
return permService.hasEditPerm(securityCtx, id)
.invoke(Unchecked.consumer(cm -> {
if (cm.getSystem() != CompetitionSystem.INTERNAL)
throw new DBadRequestException(trad.t("competition.is.not.internal"));
}))
.chain(competitionModel -> {
SimpleCompetData data = SimpleCompetData.fromModel(competitionModel);
return vertx.getOrCreateContext().executeBlocking(() -> {
if (competitionModel.getAdmin() != null)
data.setAdmin(
competitionModel.getAdmin().stream().map(uuid -> keycloakService.getUserById(uuid))
.filter(Optional::isPresent)
.map(user -> user.get().getUsername())
.toList());
if (competitionModel.getTable() != null)
data.setTable(
competitionModel.getTable().stream().map(uuid -> keycloakService.getUserById(uuid))
.filter(Optional::isPresent)
.map(user -> user.get().getUsername())
.toList());
return data;
});
});
}
public Uni<Void> setInternalData(SecurityCtx securityCtx, SimpleCompetData data) {
return permService.hasEditPerm(securityCtx, data.getId())
.invoke(Unchecked.consumer(cm -> {
if (cm.getSystem() != CompetitionSystem.INTERNAL)
throw new DBadRequestException(trad.t("competition.is.not.internal"));
}))
.chain(cm -> vertx.getOrCreateContext().executeBlocking(() -> {
ArrayList<String> admin = new ArrayList<>();
ArrayList<String> table = new ArrayList<>();
for (String username : data.getAdmin()) {
Optional<UserRepresentation> opt = keycloakService.getUser(username);
if (opt.isEmpty())
throw new DBadRequestException(String.format(trad.t("user.not.found"), username));
admin.add(opt.get().getId());
}
for (String username : data.getTable()) {
Optional<UserRepresentation> opt = keycloakService.getUser(username);
if (opt.isEmpty())
throw new DBadRequestException(String.format(trad.t("user.not.found"), username));
table.add(opt.get().getId());
}
cm.setAdmin(admin);
cm.setTable(table);
cm.setConfig(data.getConfigForInternal());
return cm;
}))
.chain(cm -> Panache.withTransaction(() -> repository.persist(cm)))
.replaceWithVoid();
}
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(String.format(trad.t("user.not.found"), username));
admin.add(UUID.fromString(opt.get().getId()));
}
for (String username : data.getTable()) {
Optional<UserRepresentation> opt = keycloakService.getUser(username);
if (opt.isEmpty())
throw new DBadRequestException(String.format(trad.t("user.not.found"), username));
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 -> {
List<Uni<Void>> list = 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 -> cacheAccess.invalidate(uuid.toString())).toList();
if (list.isEmpty())
return Uni.createFrom().nullItem();
return Uni.join().all(list).andCollectFailures();
}))
.call(__ -> cache.invalidate(data.getId()));
}
public Uni<List<PresetData>> getPresetsForCompetition(SecurityCtx securityCtx, Long id) {
return permService.hasViewPerm(securityCtx, id)
.chain(cm -> Mutiny.fetch(cm.getCatPreset()))
.map(p -> p.stream().map(PresetData::fromModel).toList());
}
public Uni<Response> unregisterHelloAsso(NotificationData data) {
if (!data.getState().equals("Refunded"))
return Uni.createFrom().item(Response.ok().build());
return helloAssoRepository.list("orderId = ?1", data.getOrder().getId())
.chain(regs -> {
Uni<?> uni = Uni.createFrom().nullItem();
for (HelloAssoRegisterModel reg : regs) {
if (reg.getCompetition().getRegisterMode() != RegisterMode.HELLOASSO)
continue;
if (!data.getOrder().getOrganizationSlug().equalsIgnoreCase(reg.getCompetition().getData1()))
continue;
uni = uni.call(__ -> Panache.withTransaction(
() -> registerRepository.delete("competition = ?1 AND membre = ?2",
reg.getCompetition(), reg.getMembre())))
.call(r -> reg.getCompetition().getSystem() == CompetitionSystem.INTERNAL ?
sRegister.sendRegisterRemove(reg.getCompetition().getUuid(),
reg.getMembre().getId()) : Uni.createFrom().voidItem()).onFailure()
.recoverWithNull()
;
}
return uni;
})
.onFailure().invoke(Throwable::printStackTrace)
.map(__ -> Response.ok().build());
}
public Uni<Response> registerHelloAsso(NotificationData data) {
String organizationSlug = data.getOrganizationSlug();
String formSlug = data.getFormSlug();
RegisterRequestData req = new RegisterRequestData(null, "", "", null, null, 0, false, new ArrayList<>(), null,
Categorie.CADET, Genre.NA, null, "fr", false);
return repository.find("data1 = ?1 AND data2 = ?2", organizationSlug, formSlug).firstResult()
.onFailure().recoverWithNull()
.chain(cm -> {
Uni<?> uni = Uni.createFrom().nullItem();
if (cm == null || cm.getRegisterMode() != RegisterMode.HELLOASSO)
return uni;
List<String> place = List.of(cm.getData3().toLowerCase().split(";"));
List<String> fail = new ArrayList<>();
for (NotificationData.Item item : data.getItems()) {
if (!place.contains(item.getName().toLowerCase()))
continue;
if (item.getCustomFields() == null || item.getCustomFields().isEmpty()) {
fail.add("%s %s - licence n°???".formatted(item.getUser().getLastName(),
item.getUser().getFirstName()));
continue;
}
Optional<Long> optional = item.getCustomFields().stream()
.filter(cf -> cf.getName().equalsIgnoreCase("Numéro de licence")).findAny().map(
NotificationData.CustomField::getAnswer).map(Long::valueOf);
if (optional.isPresent()) {
uni = uni.call(__ -> membreService.getByLicence(optional.get())
.invoke(Unchecked.consumer(m -> {
if (m == null)
throw new NotFoundException();
}))
.call(m -> Panache.withTransaction(() ->
helloAssoRepository.persist(
new HelloAssoRegisterModel(cm, m, data.getId()))))
.chain(m -> updateRegister(req, cm, m, true, true)))
.onFailure().recoverWithItem(throwable -> {
fail.add("%s %s - licence n°%d".formatted(item.getUser().getLastName(),
item.getUser().getFirstName(), optional.get()));
return null;
})
.replaceWithVoid();
} else {
fail.add("%s %s - licence n°???".formatted(item.getUser().getLastName(),
item.getUser().getFirstName()));
}
}
return uni.call(__ -> fail.isEmpty() ? Uni.createFrom().nullItem() :
reactiveMailer.send(
Mail.withText(cm.getData4(),
"FFSAF - Compétition - Erreur HelloAsso",
String.format(
"""
Bonjour,
Une erreur a été rencontrée lors de l'enregistrement d'une inscription à votre compétition %s pour les combattants suivants:
%s
Cordialement,
L'intranet de la FFSAF
""", cm.getName(), String.join("\r\n", fail))
).setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr")
).onFailure().invoke(e -> LOGGER.error("Fail to send email", e)));
})
.onFailure().invoke(Throwable::printStackTrace)
.map(__ -> Response.ok().build());
}
}

View File

@@ -1,71 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.rest.client.HelloAssoAuthClient;
import fr.titionfire.ffsaf.rest.client.dto.TokenResponse;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;
@ApplicationScoped
public class HelloAssoTokenService {
private static final Logger LOG = Logger.getLogger(HelloAssoTokenService.class);
@RestClient
HelloAssoAuthClient authClient;
@ConfigProperty(name = "helloasso.client-id")
String clientId;
@ConfigProperty(name = "helloasso.client-secret")
String clientSecret;
private TokenResponse currentToken; // Stockage en mémoire (pour un seul pod)
// Récupère un token valide (en le rafraîchissant si nécessaire)
public Uni<String> getValidAccessToken() {
if (clientId == null || clientSecret == null || clientId.isEmpty() || clientSecret.isEmpty() || clientId.equalsIgnoreCase(
"null") || clientSecret.equalsIgnoreCase("null")) {
LOG.error("Client ID ou Client Secret non configuré pour HelloAsso");
return Uni.createFrom().failure(new IllegalStateException("HelloAsso client credentials not configured"));
}
if (currentToken == null || currentToken.isExpired()) {
return fetchNewToken(clientId, clientSecret);
}
return Uni.createFrom().item(currentToken.accessToken);
}
// Récupère un nouveau token (via client_credentials ou refresh_token)
private Uni<String> fetchNewToken(String clientId, String clientSecret) {
if (currentToken != null && currentToken.refreshToken != null) {
// On utilise le refresh_token si disponible
return authClient.refreshToken("refresh_token", clientId, currentToken.refreshToken)
.onItem().invoke(token -> {
LOG.info("Token rafraîchi avec succès");
currentToken = token;
})
.onFailure().recoverWithItem(e -> {
LOG.warn("Échec du rafraîchissement, utilisation des credentials", e);
return null; // Force l'utilisation des credentials
})
.flatMap(token -> token != null ?
Uni.createFrom().item(token.accessToken) :
getTokenWithCredentials(clientId, clientSecret)
);
} else {
return getTokenWithCredentials(clientId, clientSecret);
}
}
// Récupère un token avec client_id/client_secret
private Uni<String> getTokenWithCredentials(String clientId, String clientSecret) {
return authClient.getToken("client_credentials", clientId, clientSecret)
.onItem().invoke(token -> {
LOG.info("Nouveau token obtenu");
currentToken = token;
})
.onFailure().invoke(e -> LOG.error("Erreur lors de l'obtention du token", e))
.map(token -> token.accessToken);
}
}

View File

@@ -2,10 +2,8 @@ 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.mailer.Mail;
import io.quarkus.mailer.reactive.ReactiveMailer;
import fr.titionfire.ffsaf.utils.KeycloakException;
import fr.titionfire.ffsaf.utils.RequiredAction;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked;
@@ -23,10 +21,8 @@ import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ApplicationScoped
public class KeycloakService {
@@ -44,12 +40,6 @@ public class KeycloakService {
@ConfigProperty(name = "keycloak.realm")
String realm;
@ConfigProperty(name = "email.enabled")
boolean enabled_email;
@Inject
ReactiveMailer reactiveMailer;
@Inject
Vertx vertx;
@@ -57,167 +47,58 @@ public class KeycloakService {
if (club.getClubId() == null) {
LOGGER.infof("Creation of club group %d-%s...", club.getId(), club.getName());
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")));
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")));
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(),
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()));
}
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));
}
return Uni.createFrom().item(club::getClubId);
}
public Uni<String> updateGroupFromClub(ClubModel club) {
if (club.getClubId() == null) {
return getGroupFromClub(club);
} else {
LOGGER.infof("Updating name of club group %d-%s...", club.getId(), club.getName());
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")));
keycloak.realm(realm).groups().group(clubGroup.getId()).getSubGroups(0, 1000, true).stream()
.filter(g -> g.getName().startsWith(club.getId() + "-")).findAny()
.ifPresent(groupRepresentation -> {
groupRepresentation.setName(club.getId() + "-" + club.getName());
keycloak.realm(realm).groups().group(groupRepresentation.getId())
.update(groupRepresentation);
});
return club.getClubId();
}
);
}
}
public Uni<String> getUserFromMember(MembreModel membreModel) {
if (membreModel.getUserId() == null) {
return Uni.createFrom()
.failure(new DInternalError("No keycloak user linked to the user id=" + membreModel.getId()));
return Uni.createFrom().failure(new NullPointerException("No keycloak user linked to the user id=" + membreModel.getId()));
}
return Uni.createFrom().item(membreModel::getUserId);
}
public Uni<String> setClubGroupMembre(MembreModel membreModel, ClubModel club) {
if (club == null)
return 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()));
return "OK";
}));
else
return getGroupFromClub(club).chain(
clubId -> getUserFromMember(membreModel).chain(
userId -> vertx.getOrCreateContext().executeBlocking(() -> {
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.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());
LOGGER.infof("Set club \"%s\" to user %s (%s)", club.getName(), userId, user.toRepresentation().getUsername());
return "OK";
})));
}
public Uni<?> setEmail(String userId, String email) {
return vertx.getOrCreateContext().executeBlocking(() -> {
UserResource user = keycloak.realm(realm).users().get(userId);
UserRepresentation user2 = user.toRepresentation();
String oldEmail = user2.getEmail();
if (email.equals(user2.getEmail()))
return null;
user2.setEmail(email);
user2.setRequiredActions(List.of(RequiredAction.VERIFY_EMAIL.name()));
user.update(user2);
if (enabled_email)
user.sendVerifyEmail();
return oldEmail;
}).call(oldEmail -> oldEmail == null || !enabled_email ? Uni.createFrom().item("") :
reactiveMailer.send(
Mail.withHtml(oldEmail, "FFSAF - Changement de votre adresse email",
String.format(Utils.HTML_HEADER, "FFSAF - Changement de votre adresse email") +
String.format("""
<p>Suite à la modification de votre adresse email fournie lors de votre (ré)inscription à la FFSAF,<br/>
vous allez recevoir dans les prochaines minutes un email de vérification de votre nouvelle adresse sur celle-ci.</p>
<p>Ancienne adresse email : <span class="highlight">%s</span><br/>Nouvelle adresse email : <span class="highlight">%s</span></p>
<p>Si vous n'avez pas demandé cette modification, veuillez contacter le support à l'adresse <a href="mailto:support@ffsaf.fr">support@ffsaf.fr</a>.</p>
""",
oldEmail, email) +
Utils.HTML_FOOTER
)
.setText(String.format("""
Bonjour,
Suite à la modification de votre adresse email fournie lors de votre (ré)inscription à la FFSAF,
vous allez recevoir dans les prochaines minutes un email de vérification de votre nouvelle adresse sur celle-ci.
Ancienne adresse email : %s
Nouvelle adresse email : %s
Si vous n'avez pas demandé cette modification, veuillez contacter le support à l'adresse support@ffsaf.fr.
Cordialement,
L'équipe de la FFSAF
""", oldEmail, email))
.setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr")
).onFailure().invoke(e -> LOGGER.error("Fail to send email", e)));
}
public Uni<?> setAutoRoleMembre(String id, RoleAsso role, GradeArbitrage gradeArbitrage) {
List<String> toRemove = new ArrayList<>(List.of("club_president", "club_tresorier", "club_secretaire",
"club_respo_intra", "asseseur", "arbitre"));
List<String> toAdd = new ArrayList<>();
switch (role) {
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"));
case ASSESSEUR -> toAdd.add("asseseur");
}
toRemove.removeAll(toAdd);
return updateRole(id, toAdd, toRemove);
}
public Uni<Pair<UserResource, UserCompteState>> fetchCompte(String id) {
public Uni<UserCompteState> fetchCompte(String id) {
return vertx.getOrCreateContext().executeBlocking(() -> {
UserResource user = keycloak.realm(realm).users().get(id);
UserRepresentation user2 = user.toRepresentation();
return new Pair<>(user, new UserCompteState(user2.isEnabled(), user2.getUsername(),
user2.isEmailVerified()));
return new UserCompteState(user2.isEnabled(), user2.getUsername(), user2.isEmailVerified(),
user.roles().realmLevel().listEffective().stream().map(RoleRepresentation::getName).toList(),
user.groups().stream().map(GroupRepresentation::getName).toList());
});
}
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) {
@@ -240,23 +121,15 @@ public class KeycloakService {
throw new KeycloakException("User name is null");
})).chain(membreModel -> creatUser(membreModel).chain(user -> {
LOGGER.infof("Set user id %s to membre %s", user.getId(), membreModel.getId());
return membreService.setUserId(membreModel.getId(), user.getId()).map(__ -> user.getId());
}));
return membreService.setUserId(membreModel.getId(), user.getId());
}))
.map(__ -> "OK");
}
private Uni<UserRepresentation> creatUser(MembreModel membreModel) {
return vertx.getOrCreateContext().executeBlocking(() -> {
String login;
int i = 1;
do {
login = makeLogin(membreModel);
if (i > 1) {
login += i;
}
i++;
} while (!keycloak.realm(realm).users().searchByUsername(login, true).isEmpty());
String login = makeLogin(membreModel);
LOGGER.infof("Creation of user %s...", login);
return vertx.getOrCreateContext().executeBlocking(() -> {
UserRepresentation user = new UserRepresentation();
user.setUsername(login);
user.setFirstName(membreModel.getFname());
@@ -264,109 +137,22 @@ public class KeycloakService {
user.setEmail(membreModel.getEmail());
user.setEnabled(true);
user.setRequiredActions(List.of(RequiredAction.VERIFY_EMAIL.name(),
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))
throw new KeycloakException("Fail to creat user %s (reason=%s)".formatted(login,
response.getStatusInfo().getReasonPhrase()));
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(login)));
})
.invoke(user -> membreModel.setUserId(user.getId()))
.call(user -> updateRole(user.getId(), List.of("safca_user"), List.of()))
.call(user -> enabled_email ? reactiveMailer.send(
Mail.withHtml(user.getEmail(), "FFSAF - Creation de votre compte sur l'intranet",
String.format(Utils.HTML_HEADER, "Création de votre compte intranet FFSAF") +
String.format(
"""
<p>Suite &agrave; votre premi&egrave;re inscription <span class="highlight">%s</span> &agrave; la <strong>F&eacute;d&eacute;ration France Soft Armored Fighting (FFSAF)</strong>, votre compte intranet a &eacute;t&eacute; cr&eacute;&eacute;.</p>
<p>Ce compte vous permettra de :</p>
<ul>
<li>Consulter vos informations personnelles,</li>
<li>Vous inscrire aux comp&eacute;titions (bient&ocirc;t disponible),</li>
<li>Consulter les r&eacute;sultats des comp&eacute;titions.</li>
</ul>
<p>L&rsquo;intranet est accessible &agrave; l&rsquo;adresse suivante :</p>
<p style="text-align: center;"><a href="https://intra.ffsaf.fr" class="button">Acc&eacute;der &agrave; l&apos;intranet</a></p>
<p>Votre nom d&rsquo;utilisateur est : <span class="highlight">%s</span></p>
<p>Pour d&eacute;finir votre mot de passe, rendez-vous sur l&rsquo;intranet : <strong>Connexion &gt; Mot de passe oubli&eacute; ?</strong></p>
<p>Si vous n&rsquo;avez pas demand&eacute; cette inscription, veuillez contacter le support &agrave; l&rsquo;adresse : <a href="mailto:support@ffsaf.fr">support@ffsaf.fr</a>.</p>
<p class="footer">(Pas de panique, nous ne vous enverrons pas de message autre que ceux concernant votre compte.)</p>
""",
membreModel.getRole() == RoleAsso.MEMBRE ? "par votre club (" + membreModel.getClub()
.getName() + ") " : "", user.getUsername()) +
Utils.HTML_FOOTER
)
.setText(String.format(
"""
Bonjour,
Suite à votre première inscription %sà la Fédération France Soft Armored Fighting (FFSAF), votre compte intranet a été créé.
Ce compte vous permettra de consulter vos informations et, dans un futur proche, de vous inscrire aux compétitions ainsi que d'en consulter les résultats.
L'intranet est accessible à l'adresse suivante : https://intra.ffsaf.fr
Votre nom d'utilisateur est : %s
Pour définir votre mot de passe, rendez-vous sur l'intranet > "Connexion" > "Mot de passe oublié ?"
Si vous n'avez pas demandé cette inscription, veuillez contacter le support à l'adresse support@ffsaf.fr.
(Pas de panique, nous ne vous enverrons pas de message autre que ce concernant votre compte)
Cordialement,
L'équipe de la FFSAF
""",
membreModel.getRole() == RoleAsso.MEMBRE ? "par votre club (" + membreModel.getClub()
.getName() + ") " : "", user.getUsername()))
.setFrom("FFSAF <no-reply@ffsaf.fr>").setReplyTo("support@ffsaf.fr")
) : Uni.createFrom().nullItem())
.call(user -> membreService.setUserId(membreModel.getId(), user.getId()))
.call(user -> setClubGroupMembre(membreModel, membreModel.getClub()));
}
public Uni<?> setId(long id, String nid) {
return membreService.setUserId(id, nid).map(__ -> "OK");
}
public Uni<?> removeAccount(String userId) {
return vertx.getOrCreateContext().executeBlocking(() -> {
try (Response response = keycloak.realm(realm).users().delete(userId)) {
System.out.println(response.getStatusInfo());
if (!response.getStatusInfo().equals(Response.Status.NO_CONTENT))
throw new KeycloakException("Fail to delete user %s (reason=%s)".formatted(userId,
response.getStatusInfo().getReasonPhrase()));
}
return null;
});
}
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) {
private Optional<UserRepresentation> getUser(String username) {
List<UserRepresentation> users = keycloak.realm(realm).users().searchByUsername(username, true);
if (users.isEmpty())
@@ -375,32 +161,14 @@ public class KeycloakService {
return Optional.of(users.get(0));
}
public Optional<UserRepresentation> getUser(UUID userId) {
return getUserById(userId.toString());
}
public Optional<UserRepresentation> getUserById(String userId) {
try {
UserResource user = keycloak.realm(realm).users().get(userId);
if (user == null)
return Optional.empty();
else
return Optional.of(user.toRepresentation());
} catch (Exception e) {
return Optional.empty();
}
}
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}", "");
}
@RegisterForReflection
public record UserCompteState(Boolean enabled, String login, Boolean emailVerified) {
public record UserCompteState(Boolean enabled, String login, Boolean emailVerified, List<String> realmRoles,
List<String> groups) {
}
}

View File

@@ -1,258 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.LicenceModel;
import fr.titionfire.ffsaf.data.model.LogModel;
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;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.hibernate.reactive.mutiny.Mutiny;
import org.jboss.logging.Logger;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
@WithSession
@ApplicationScoped
public class LicenceService {
private static final Logger LOGGER = Logger.getLogger(LicenceService.class);
@Inject
LicenceRepository repository;
@Inject
CombRepository combRepository;
@Inject
SequenceRepository sequenceRepository;
@Inject
KeycloakService keycloakService;
@Inject
LoggerService ls;
@Inject
CheckoutService checkoutService;
@Inject
TradService trad;
public Uni<List<LicenceModel>> getLicence(long id, Consumer<MembreModel> checkPerm) {
return combRepository.findById(id).invoke(checkPerm)
.chain(combRepository -> Mutiny.fetch(combRepository.getLicences()));
}
public Uni<List<LicenceModel>> getCurrentSaisonLicence(SecurityCtx securityCtx) {
if (securityCtx == null || securityCtx.getSubject() == null)
return repository.find("saison = ?1", Utils.getSaison()).list();
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<?> valideLicences(List<Long> ids) {
Uni<String> uni = Uni.createFrom().nullItem();
for (Long id : ids) {
uni = uni.chain(__ -> repository.find("membre.id = ?1 AND saison = ?2", id, Utils.getSaison()).firstResult()
.chain(model -> {
if (!model.isValidate())
ls.logUpdate("validation de la licence", model);
return validateLicences(model);
}))
.map(__ -> "OK");
}
return uni.call(__ -> ls.append());
}
protected Uni<LicenceModel> validateLicences(LicenceModel model) {
model.setValidate(true);
return Panache.withTransaction(() -> repository.persist(model)
.call(m -> Mutiny.fetch(m.getMembre())
.call(genLicenceNumberAndAccountIfNeed())
));
}
public Uni<?> validePaymentLicences(List<Long> ids) {
Uni<String> uni = Uni.createFrom().nullItem();
for (Long id : ids) {
uni = uni.chain(__ -> repository.find("membre.id = ?1 AND saison = ?2", id, Utils.getSaison()).firstResult()
.chain(model -> {
if (!model.isPay())
ls.logUpdate("payment (admin) de la licence", model);
return validatePayLicences(model);
}))
.map(__ -> "OK");
}
return uni.call(__ -> ls.append());
}
protected Uni<LicenceModel> validatePayLicences(LicenceModel model) {
model.setPay(true);
return Panache.withTransaction(() -> repository.persist(model));
}
public Uni<LicenceModel> setLicence(long id, LicenceForm form) {
if (form.getId() == -1) {
return combRepository.findById(id).chain(membreModel -> {
LicenceModel model = new LicenceModel();
model.setMembre(membreModel);
model.setClub_id((membreModel.getClub() == null) ? null : membreModel.getClub().getId());
model.setSaison(form.getSaison());
model.setCertificate(form.getCertificate());
model.setValidate(form.isValidate());
model.setPay(form.isPay());
return Panache.withTransaction(() -> repository.persist(model)
.call(m -> m.isValidate() ? Uni.createFrom().item(membreModel)
.call(genLicenceNumberAndAccountIfNeed())
: Uni.createFrom().nullItem()
))
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, membreModel.getObjectName(),
licenceModel));
});
} else {
return repository.findById(form.getId()).chain(model -> {
ls.logChange("Certificate", model.getCertificate(), form.getCertificate(), model);
ls.logChange("Validate", model.isValidate(), form.isValidate(), model);
ls.logChange("Pay", model.isPay(), form.isPay(), model);
model.setCertificate(form.getCertificate());
model.setValidate(form.isValidate());
model.setPay(form.isPay());
return Panache.withTransaction(() -> repository.persist(model)
.call(m -> m.isValidate() ? Mutiny.fetch(m.getMembre())
.call(genLicenceNumberAndAccountIfNeed())
: Uni.createFrom().nullItem()
))
.call(__ -> ls.append());
});
}
}
private Function<MembreModel, Uni<?>> genLicenceNumberAndAccountIfNeed() {
return membreModel -> ((membreModel.getLicence() == null || membreModel.getLicence() <= 0) ?
sequenceRepository.getNextValueInTransaction(SequenceType.Licence)
.invoke(i -> membreModel.setLicence(Math.toIntExact(i)))
.chain(() -> combRepository.persist(membreModel))
: Uni.createFrom().nullItem())
.call(__ -> (membreModel.getUserId() == null) ?
keycloakService.initCompte(membreModel.getId()).onFailure()
.invoke(t -> LOGGER.infof("Failed to init account: %s", t.getMessage())).onFailure()
.recoverWithNull()
: Uni.createFrom().nullItem());
}
public Uni<String> payLicences(List<Long> ids, Consumer<MembreModel> checkPerm, SecurityCtx securityCtx) {
return repository.list("membre.id IN ?1 AND saison = ?2 AND pay = FALSE", ids, Utils.getSaison())
.invoke(Unchecked.consumer(models -> {
if (models.size() != ids.size())
throw new DBadRequestException(trad.t("erreur.lors.de.la.selection.des.membres"));
}))
.call(models -> {
Uni<?> uni = Uni.createFrom().nullItem();
for (LicenceModel model : models)
uni = uni.chain(__ -> Mutiny.fetch(model.getMembre()).invoke(checkPerm));
return uni;
})
.chain(models -> checkoutService.create(models.stream().map(LicenceModel::getId).toList(),
securityCtx));
}
public Uni<?> deleteLicence(long id) {
return repository.findById(id)
.call(__ -> checkoutService.canDeleteLicence(id)
.invoke(Unchecked.consumer(b -> {
if (!b) throw new DBadRequestException(
trad.t("licence.rm.err1"));
})))
.call(model -> ls.logADelete(model))
.chain(model -> Panache.withTransaction(() -> repository.delete(model)));
}
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 DBadRequestException(trad.t("licence.deja.demandee"));
})).chain(__ -> combRepository.findById(id).chain(membreModel2 -> {
LicenceModel model = new LicenceModel();
model.setClub_id((membreModel2.getClub() == null) ? null : membreModel2.getClub().getId());
model.setMembre(membreModel2);
model.setSaison(Utils.getSaison());
model.setCertificate(form.getCertificate());
model.setValidate(false);
return Panache.withTransaction(() -> repository.persist(model));
}))
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, membreModel.getObjectName(),
licenceModel));
} else {
return repository.findById(form.getId()).chain(model -> {
ls.logChange("Certificate", model.getCertificate(), form.getCertificate(), model);
model.setCertificate(form.getCertificate());
return Panache.withTransaction(() -> repository.persist(model))
.call(__ -> ls.append());
});
}
});
}
public Uni<?> deleteAskLicence(long id, Consumer<MembreModel> checkPerm) {
return repository.findById(id)
.call(licenceModel -> Mutiny.fetch(licenceModel.getMembre()).invoke(checkPerm))
.call(__ -> checkoutService.canDeleteLicence(id)
.invoke(Unchecked.consumer(b -> {
if (!b) throw new DBadRequestException(
trad.t("licence.rm.err1"));
})))
.invoke(Unchecked.consumer(licenceModel -> {
if (licenceModel.isValidate())
throw new DBadRequestException(trad.t("impossible.de.supprimer.une.licence.deja.validee"));
if (licenceModel.isPay())
throw new DBadRequestException(trad.t("impossible.de.supprimer.une.licence.deja.payee"));
}))
.call(model -> ls.logADelete(model))
.chain(__ -> Panache.withTransaction(() -> repository.deleteById(id)));
}
public Uni<?> setImport(int licence, int saison, boolean valid) {
return combRepository.find("licence = ?1", licence).firstResult()
.chain(membreModel ->
repository.find("saison = ?1 AND membre = ?2", saison, membreModel).firstResult()
.chain(licenceModel -> {
if (licenceModel != null) {
if (licenceModel.getClub_id() == null)
licenceModel.setClub_id(
(membreModel.getClub() == null) ? null : membreModel.getClub()
.getId());
licenceModel.setValidate(valid);
return Panache.withTransaction(() -> repository.persist(licenceModel));
} else {
LicenceModel model = new LicenceModel();
model.setClub_id(
(membreModel.getClub() == null) ? null : membreModel.getClub().getId());
model.setMembre(membreModel);
model.setSaison(saison);
model.setCertificate("¤");
model.setValidate(valid);
return Panache.withTransaction(() -> repository.persist(model));
}
}))
.map(__ -> "OK");
}
}

View File

@@ -1,111 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.LogModel;
import fr.titionfire.ffsaf.data.model.LogModel.ActionType;
import fr.titionfire.ffsaf.data.model.LogModel.ObjectType;
import fr.titionfire.ffsaf.data.model.LoggableModel;
import fr.titionfire.ffsaf.data.repository.LogRepository;
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.RequestScoped;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@WithSession
@RequestScoped
public class LoggerService {
@Inject
LogRepository repository;
@Inject
SecurityCtx securityCtx;
private final List<LogModel> buffer = new ArrayList<>();
public Uni<?> logA(ActionType action, ObjectType object, String message, String target_name, Long target_id) {
return Panache.withTransaction(() -> repository.persist(
new LogModel(null, securityCtx.getSubject(), new Date(), action, object, target_id, target_name,
message)));
}
public Uni<?> logA(ActionType action, String message, LoggableModel model) {
return logA(action, model.getObjectType(), message, model.getObjectName(), model.getId());
}
public Uni<?> logAAdd(LoggableModel model) {
return logA(ActionType.ADD, "", model);
}
public Uni<?> logAUpdate(String message, LoggableModel model) {
return logA(ActionType.UPDATE, message, model);
}
public Uni<?> logAChange(String champ, Object o1, Object o2, LoggableModel model) {
if (Objects.equals(o1, o2))
return Uni.createFrom().nullItem();
return logA(ActionType.UPDATE, champ + ": " + o1.toString() + " -> " + o2.toString(), model);
}
public Uni<?> logADelete(LoggableModel model) {
return logA(ActionType.REMOVE, "", model);
}
public Uni<?> append() {
if (buffer.isEmpty())
return Uni.createFrom().voidItem();
return Panache.withTransaction(() -> repository.persist(buffer))
.invoke(__ -> buffer.clear());
}
public void clear() {
buffer.clear();
}
public void log(ActionType action, ObjectType object, String message, String target_name, Long target_id) {
buffer.add(new LogModel(null, securityCtx.getSubject(), new Date(), action, object, target_id, target_name,
message));
}
public void logAnonymous(ActionType action, ObjectType object, String message, String target_name, Long target_id) {
buffer.add(new LogModel(null, null, new Date(), action, object, target_id, target_name, message));
}
public void log(ActionType action, String message, LoggableModel model) {
log(action, model.getObjectType(), message, model.getObjectName(), model.getId());
}
public void logAnonymous(ActionType action, String message, LoggableModel model) {
logAnonymous(action, model.getObjectType(), message, model.getObjectName(), model.getId());
}
public void logAdd(LoggableModel model) {
log(ActionType.ADD, "", model);
}
public void logUpdate(String message, LoggableModel model) {
log(ActionType.UPDATE, message, model);
}
public void logUpdateAnonymous(String message, LoggableModel model) {
logAnonymous(ActionType.UPDATE, message, model);
}
public void logChange(String champ, Object o1, Object o2, LoggableModel model) {
if (Objects.equals(o1, o2))
return;
log(ActionType.UPDATE,
champ + ": " + (o1 == null ? "null" : o1.toString()) + " -> " + (o2 == null ? "null" : o2.toString()),
model);
}
public void logDelete(LoggableModel model) {
log(ActionType.REMOVE, "", model);
}
}

View File

@@ -1,132 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.CompetitionGuestModel;
import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.repository.CategoryRepository;
import fr.titionfire.ffsaf.data.repository.CombRepository;
import fr.titionfire.ffsaf.data.repository.CompetitionGuestRepository;
import fr.titionfire.ffsaf.data.repository.MatchRepository;
import fr.titionfire.ffsaf.rest.data.MatchData;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
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
CategoryRepository categoryRepository;
@Inject
CombRepository combRepository;
@Inject
CompetPermService permService;
@Inject
CompetitionGuestRepository competitionGuestRepository;
public Uni<MatchData> getByIdAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
.onItem().ifNull().failWith(() -> new DNotFoundException("Match not found"))
.call(data -> permService.hasAdminViewPerm(securityCtx, data.getCategory().getCompet()))
.map(MatchData::fromModel);
}
public Uni<List<MatchData>> getAllByPouleAdmin(SecurityCtx securityCtx, CompetitionSystem system, Long id) {
return categoryRepository.find("systemId = ?1 AND system = ?2", id, system).firstResult()
.onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
.call(data -> permService.hasAdminViewPerm(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 categoryRepository.find("systemId = ?1 AND system = ?2", data.getCategory(), system)
.firstResult()
.onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
.map(categoryModel -> {
MatchModel model = new MatchModel();
model.setId(null);
model.setSystem(system);
model.setSystemId(data.getId());
model.setCategory(categoryModel);
return model;
});
} else {
return categoryRepository.find("systemId = ?1 AND system = ?2", data.getCategory(), system)
.firstResult()
.onItem().ifNull().failWith(() -> new DNotFoundException("Poule not found"))
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet()))
.map(__ -> o);
}
}
)
.chain(o -> {
o.setCategory_ord(data.getCategory_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(() -> (data.getC1_str() == null) ?
Uni.createFrom()
.item((CompetitionGuestModel) null) : competitionGuestRepository.find(
"fname = ?1 AND lname = ?2",
data.getC1_str().substring(0, data.getC1_str().indexOf(" ")),
data.getC1_str().substring(data.getC1_str().indexOf(" ") + 1)).firstResult())
.invoke(o::setC1_guest)
.chain(() -> (data.getC2_str() == null) ?
Uni.createFrom()
.item((CompetitionGuestModel) null) : competitionGuestRepository.find(
"fname = ?1 AND lname = ?2",
data.getC2_str().substring(0, data.getC2_str().indexOf(" ")),
data.getC2_str().substring(data.getC2_str().indexOf(" ") + 1)).firstResult())
.invoke(o::setC2_guest)
.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 DNotFoundException("Match not found"))
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCategory().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 DNotFoundException("Match not found"))
.call(o2 -> permService.hasEditPerm(securityCtx, o2.getCategory().getCompet()))
.chain(data -> Panache.withTransaction(() -> repository.delete(data)));
}
}

View File

@@ -1,627 +1,82 @@
package fr.titionfire.ffsaf.domain.service;
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.*;
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.SimpleCombModel;
import fr.titionfire.ffsaf.net2.request.SReqComb;
import fr.titionfire.ffsaf.rest.data.*;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
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.rest.from.UserSettingForm;
import fr.titionfire.ffsaf.utils.*;
import fr.titionfire.ffsaf.utils.Pair;
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.scheduler.Scheduled;
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 org.jboss.logging.Logger;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.List;
@WithSession
@ApplicationScoped
public class MembreService {
private static final Logger LOGGER = Logger.getLogger(MembreService.class);
@Inject
CombRepository repository;
@Inject
ClubRepository clubRepository;
@Inject
LicenceRepository licenceRepository;
@Inject
CompetitionRepository competitionRepository;
@Inject
ServerCustom serverCustom;
@Inject
KeycloakService keycloakService;
@ConfigProperty(name = "upload_dir")
String media;
@Inject
RegisterRepository registerRepository;
@Inject
LoggerService ls;
@Inject
TradService trad;
public SimpleCombModel find(int licence, String np) throws Throwable {
return VertxContextSupport.subscribeAndAwait(() -> Panache.withTransaction(() ->
repository.find(
"licence = ?1 AND (unaccent(lname) ILIKE unaccent(?2) OR unaccent(fname) ILIKE unaccent(?2))",
repository.find("licence = ?1 AND (lname collate UTF8_GENERAL_CI LIKE ?2 OR fname collate UTF8_GENERAL_CI LIKE ?2)",
licence, np).firstResult().map(SimpleCombModel::fromModel)));
}
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)));
}
final static String FIND_NAME_REQUEST = "unaccent(fname) ILIKE unaccent(?1) OR unaccent(lname) ILIKE unaccent(?1) " +
"OR unaccent(fname || ' ' || lname) ILIKE unaccent(?1) OR unaccent(lname || ' ' || fname) ILIKE unaccent(?1)";
private Uni<List<LicenceModel>> getLicenceListe(int licenceRequest, int payState) {
Uni<List<LicenceModel>> baseUni;
String queryStr = "saison = ?1";
if (payState == 0)
queryStr += " AND pay = FALSE";
if (payState == 1)
queryStr += " AND pay = TRUE";
if (licenceRequest == 0 || licenceRequest == 1)
baseUni = licenceRepository.list(queryStr, Utils.getSaison());
else if (licenceRequest == 2)
baseUni = licenceRepository.list(queryStr + " AND validate = FALSE", Utils.getSaison());
else if (licenceRequest == 5)
baseUni = licenceRepository.list(queryStr + " AND validate = FALSE AND LENGTH(certificate) >= 3",
Utils.getSaison());
else if (licenceRequest == 6)
baseUni = licenceRepository.list(queryStr + " AND validate = FALSE AND LENGTH(certificate) <= 2",
Utils.getSaison());
else if (licenceRequest == 3)
baseUni = licenceRepository.list(queryStr + " AND validate = TRUE", Utils.getSaison());
else
baseUni = Uni.createFrom().item(new ArrayList<>());
return baseUni;
}
private Sort getSort(String order) {
Sort sort;
if (order == null || order.isBlank()) {
sort = Sort.ascending("fname", "lname");
} else {
sort = Sort.empty();
for (String e : order.split(",")) {
String[] split = e.split(" ");
if (split.length == 2) {
sort = sort.and(split[0],
split[1].equals("n") ? Sort.Direction.Ascending : Sort.Direction.Descending);
} else {
return null;
}
}
}
return sort;
}
public Uni<PageResult<SimpleMembre>> search(int limit, int page, String search, int licenceRequest, int payState,
String order, String categorie, Boolean archive, String subject) {
return repository.find("userId = ?1", subject).firstResult()
.invoke(Unchecked.consumer(membreModel -> {
if (membreModel == null || membreModel.getClub() == null)
throw new DForbiddenException();
}))
.chain(membreModel ->
searchAdmin(limit, page, search, membreModel.getClub(), licenceRequest, payState, order,
categorie, archive));
}
public Uni<PageResult<SimpleMembre>> searchAdmin(int limit, int page, String search, Object club,
int licenceRequest, int payState, String order, String categorie,
Boolean archive) {
if (search == null)
search = "";
search = "%" + search.replaceAll(" ", "% %") + "%";
String categorieFilter;
if (categorie == null || categorie.isBlank())
categorieFilter = " True";
else
categorieFilter = "categorie = " + Categorie.valueOf(categorie).ordinal();
String archiveFilter;
if (archive == null || archive)
archiveFilter = "True";
else
archiveFilter = String.format(
"EXISTS (SELECT 1 FROM LicenceModel l WHERE l.membre.id = m.id AND l.saison >= %s)",
Utils.getSaison() - 1);
String clubFilter = "(TRUE OR ?3 = ?3)";
if (club != null) {
if (club instanceof String club_) {
if (!club_.isBlank()) {
if (club_.equals("null"))
clubFilter = "club IS NULL AND ?3 = ?3";
else
clubFilter = "LOWER(club.name) LIKE LOWER(?3)";
}
} else if (club instanceof ClubModel)
clubFilter = "club = ?3";
}
String queryStr = String.format(
"SELECT m FROM MembreModel m WHERE id %s ?2 AND %s AND (%s) AND %s AND %s",
(licenceRequest == 0 || licenceRequest == 4) ? "NOT IN" : "IN", clubFilter, FIND_NAME_REQUEST,
categorieFilter,
archiveFilter);
Sort sort = getSort(order);
if (sort == null)
return Uni.createFrom().failure(new DInternalError(trad.t("erreur.lors.calcul.du.trie")));
String finalSearch = search;
return getLicenceListe(licenceRequest, payState)
.map(l -> l.stream().map(l2 -> l2.getMembre().getId()).toList())
.chain(ids -> {
PanacheQuery<MembreModel> query;
query = repository.find(queryStr, sort, finalSearch, ids, club).page(Page.ofSize(limit));
return getPageResult(query, limit, page);
});
}
private Uni<PageResult<SimpleMembre>> getPageResult(PanacheQuery<MembreModel> query, int limit, int page) {
return Uni.createFrom().item(new PageResult<SimpleMembre>())
.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(trad.t("page.out.of.range"));
}))
.invoke(result::setPage_count))
.call(result -> query.page(Page.of(page, limit)).list()
.map(membreModels -> membreModels.stream().map(SimpleMembre::fromModel).toList())
.invoke(result::setResult));
}
public Uni<List<SimpleMembreInOutData>> getAllExport(String subject) {
return repository.find("userId = ?1", subject).firstResult()
.chain(membreModel -> repository.list("club = ?1", membreModel.getClub()))
.chain(membres -> licenceRepository.list("saison = ?1 AND membre IN ?2", Utils.getSaison(), membres)
.map(l -> membres.stream().map(m -> SimpleMembreInOutData.fromModel(m, l)).toList()));
}
public Uni<String> allImporte(String subject, List<SimpleMembreInOutData> data) {
if (data == null)
return Uni.createFrom().nullItem();
final List<SimpleMembreInOutData> data2 = data.stream()
.filter(dataIn -> dataIn.getNom() != null && !dataIn.getNom()
.isBlank() && dataIn.getPrenom() != null && !dataIn.getPrenom().isBlank()).toList();
if (data2.isEmpty())
return Uni.createFrom().nullItem();
AtomicReference<ClubModel> clubModel = new AtomicReference<>();
LOGGER.debugf("Membre import (size=%d)", data2.size());
for (SimpleMembreInOutData simpleMembreInOutData : data2) {
LOGGER.debugf("-> %s", simpleMembreInOutData.toString());
}
return repository.find("userId = ?1", subject).firstResult()
.chain(membreModel -> {
clubModel.set(membreModel.getClub());
if (data2.stream().noneMatch(d -> d.getLicence() != null))
return Uni.createFrom().item(new ArrayList<MembreModel>());
return repository.list("licence IN ?1 OR LOWER(lname || ' ' || fname) IN ?2 OR email IN ?3",
data2.stream().map(SimpleMembreInOutData::getLicence).filter(Objects::nonNull).toList(),
data2.stream().map(o -> (o.getNom() + " " + o.getPrenom()).toLowerCase()).toList(),
data2.stream().map(SimpleMembreInOutData::getEmail).filter(o -> o != null && !o.isBlank())
.toList());
})
.call(Unchecked.function(membres -> {
for (MembreModel membreModel : membres) {
if (!Objects.equals(membreModel.getClub(), clubModel.get())) {
LOGGER.info("Similar membres found: " + membreModel);
throw new DForbiddenException(String.format(trad.t("le.membre.appartient.pas.a.votre.club"),
membreModel.getLicence()));
}
}
Uni<Void> uniResult = Uni.createFrom().voidItem();
for (SimpleMembreInOutData dataIn : data2) {
MembreModel model = membres.stream()
.filter(m -> (dataIn.getLicence() != null && Objects.equals(m.getLicence(),
dataIn.getLicence())) || m.getLname().equals(dataIn.getNom()) && m.getFname()
.equals(dataIn.getPrenom()) || (dataIn.getEmail() != null && !dataIn.getEmail()
.isBlank() && Objects.equals(m.getFname(), dataIn.getEmail()))).findFirst()
.orElseGet(() -> {
MembreModel mm = new MembreModel();
mm.setClub(clubModel.get());
mm.setLicences(new ArrayList<>());
mm.setCountry("FR");
return mm;
});
if (model.getId() != null) {
LOGGER.debugf("updating -> %s", dataIn.toString());
} else {
LOGGER.debugf("creating -> %s", dataIn.toString());
}
if (model.getEmail() != null && !model.getEmail().isBlank()) {
if (model.getLicence() != null && !model.getLicence().equals(dataIn.getLicence())) {
LOGGER.info("Similar membres found: " + model);
throw new DBadRequestException(
String.format(trad.t("email.deja.utilise.par"), model.getEmail(),
model.getFname(), model.getLname()));
}
if (StringSimilarity.similarity(model.getLname().toUpperCase(),
dataIn.getNom().toUpperCase()) > 3 || StringSimilarity.similarity(
model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3) {
LOGGER.info("Similar membres found: " + model);
throw new DBadRequestException(
String.format(trad.t("email.deja.utilise.par"), model.getEmail(),
model.getFname(), model.getLname()));
}
}
boolean add = model.getId() == null;
if ((!add && StringSimilarity.similarity(model.getLname().toUpperCase(),
dataIn.getNom().toUpperCase()) > 3) || (!add && StringSimilarity.similarity(
model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3)) {
LOGGER.info("Similar membres found: " + model);
throw new DBadRequestException(
String.format(trad.t("try.edit.licence"), model.getLicence(), model.getFname(),
model.getFname()));
}
ls.logChange("Nom", model.getLname(), dataIn.getNom().toUpperCase(), model);
ls.logChange("Prénom", model.getFname(),
dataIn.getPrenom().toUpperCase().charAt(0) + dataIn.getPrenom().substring(1), model);
model.setLname(dataIn.getNom().toUpperCase());
model.setFname(Utils.formatPrenom(dataIn.getPrenom()));
if (dataIn.getEmail() != null && !dataIn.getEmail().isBlank()) {
ls.logChange("Email", model.getEmail(), dataIn.getEmail(), model);
model.setEmail(dataIn.getEmail());
}
model.setGenre(Genre.fromString(dataIn.getGenre()));
if (dataIn.getBirthdate() != null) {
if (model.getBirth_date() == null || !Objects.equals(model.getBirth_date().getTime(),
dataIn.getBirthdate().getTime()))
ls.logChange("Date de naissance", model.getBirth_date(), dataIn.getBirthdate(), model);
model.setBirth_date(dataIn.getBirthdate());
model.setCategorie(Utils.getCategoryFormBirthDate(model.getBirth_date(), new Date()));
}
uniResult = uniResult
.call(() -> Panache.withTransaction(() -> repository.persist(model)
.chain(membreModel1 -> dataIn.isLicenceCurrent() ? licenceRepository.find(
"membre.id = ?1 AND saison = ?2", membreModel1.getId(),
Utils.getSaison())
.firstResult()
.call(l -> {
if (l == null) {
l = new LicenceModel();
l.setMembre(membreModel1);
l.setClub_id(clubModel.get().getId());
l.setValidate(false);
l.setSaison(Utils.getSaison());
}
l.setCertificate(dataIn.getCertif());
return licenceRepository.persist(l);
}) : licenceRepository.delete(
"membre = ?1 AND saison = ?2 AND validate = false", membreModel1,
Utils.getSaison()))));
if (add)
uniResult = uniResult.call(() -> ls.logAAdd(model));
else
uniResult = uniResult.call(() -> ls.append());
}
return uniResult;
}))
.map(__ -> "OK");
public Uni<List<MembreModel>> getAll() {
return repository.listAll(Sort.ascending("fname", "lname"));
}
public Uni<MembreModel> getById(long id) {
return repository.findById(id);
}
public Uni<MembreModel> getByIdWithLicence(long id) {
return repository.findById(id)
.call(m -> Mutiny.fetch(m.getLicences()));
}
public Uni<MembreModel> getByAccountId(String subject) {
return repository.find("userId = ?1", subject).firstResult();
}
public Uni<MembreModel> getByLicence(long licence) {
return repository.find("licence = ?1", licence).firstResult();
}
public Uni<String> update(long id, FullMemberForm membre) {
return update(repository.findById(id)
.call(__ -> repository.count("email LIKE ?1 AND id != ?2", membre.getEmail(), id)
.invoke(Unchecked.consumer(c -> {
if (c > 0 && !membre.getEmail().isBlank())
throw new DBadRequestException(trad.t("email.deja.utilise"));
})))
.chain(membreModel -> clubRepository.findById(membre.getClub())
.map(club -> new Pair<>(membreModel, club)))
.onItem().transform(pair -> {
return repository.findById(id)
.chain(membreModel -> clubRepository.findById(membre.getClub()).map(club -> new Pair<>(membreModel, club)))
.onItem().transformToUni(pair -> {
MembreModel m = pair.getKey();
ls.logChange("Rôle", m.getRole(), membre.getRole(), m);
m.setRole(membre.getRole());
ls.logChange("Club", m.getClub(), pair.getValue(), m);
m.setFname(membre.getFname());
m.setLname(membre.getLname());
m.setClub(pair.getValue());
ls.logChange("Grade d'arbitrage", m.getGrade_arbitrage(), membre.getGrade_arbitrage(), m);
m.setCountry(membre.getCountry());
m.setBirth_date(membre.getBirth_date());
m.setGenre(membre.getGenre());
m.setCategorie(membre.getCategorie());
m.setRole(membre.getRole());
m.setGrade_arbitrage(membre.getGrade_arbitrage());
return m;
}), membre, true);
}
public Uni<String> update(long id, FullMemberForm membre, SecurityCtx securityCtx) {
return update(repository.findById(id)
.call(__ -> repository.count("email LIKE ?1 AND id != ?2", membre.getEmail(), id)
.invoke(Unchecked.consumer(c -> {
if (c > 0 && !membre.getEmail().isBlank())
throw new DBadRequestException(trad.t("email.deja.utilise"));
})))
.invoke(Unchecked.consumer(membreModel -> {
if (!securityCtx.isInClubGroup(membreModel.getClub().getId()))
throw new DForbiddenException();
if (StringSimilarity.similarity(membreModel.getLname().toUpperCase(),
membre.getLname().toUpperCase()) > 3 || StringSimilarity.similarity(
membreModel.getFname().toUpperCase(), membre.getFname().toUpperCase()) > 3) {
throw new DBadRequestException(
trad.t("regiter.new.membre"));
}
}))
.invoke(Unchecked.consumer(membreModel -> {
RoleAsso source = RoleAsso.MEMBRE;
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(trad.t("permission.insuffisante"));
}))
.onItem().transform(target -> {
if (!securityCtx.getSubject().equals(target.getUserId())) {
ls.logChange("Rôle", target.getRole(), membre.getRole(), target);
target.setRole(membre.getRole());
}
return target;
}), membre, false);
}
private Uni<String> update(Uni<MembreModel> uni, FullMemberForm membre, boolean admin) {
return uni.chain(target -> {
ls.logChange("Prénom", target.getFname(), membre.getFname(), target);
target.setFname(Utils.formatPrenom(membre.getFname()));
ls.logChange("Nom", target.getLname(), membre.getLname(), target);
target.setLname(membre.getLname().toUpperCase());
ls.logChange("Pays", target.getCountry(), membre.getCountry(), target);
target.setCountry(membre.getCountry());
if (membre.getBirth_date() != null && (target.getBirth_date() == null || !Objects.equals(
target.getBirth_date().getTime(), membre.getBirth_date().getTime()))) {
ls.logChange("Date de naissance", target.getBirth_date(), membre.getBirth_date(), target);
target.setBirth_date(membre.getBirth_date());
target.setCategorie(Utils.getCategoryFormBirthDate(membre.getBirth_date(), new Date()));
}
ls.logChange("Genre", target.getGenre(), membre.getGenre(), target);
target.setGenre(membre.getGenre());
ls.logChange("Email", target.getEmail(), membre.getEmail(), target);
target.setEmail(membre.getEmail());
return Panache.withTransaction(() -> repository.persist(target)).call(() -> ls.append());
m.setEmail(membre.getEmail());
return Panache.withTransaction(() -> repository.persist(m));
})
.invoke(membreModel -> SReqComb.sendIfNeed(serverCustom.clients,
SimpleCombModel.fromModel(membreModel)))
.call(membreModel -> (admin && membreModel.getUserId() != null) ?
((membreModel.getClub() != null) ?
keycloakService.setClubGroupMembre(membreModel, membreModel.getClub()) :
keycloakService.clearUser(membreModel.getUserId()))
: Uni.createFrom().nullItem())
.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())
.call(membreModel -> {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, -7);
Date dateLimit = calendar.getTime();
return competitionRepository.list("date > ?1", dateLimit)
.call(l -> l.isEmpty() ? Uni.createFrom().nullItem() :
Uni.join().all(l.stream().map(competitionModel ->
registerRepository.update(
"categorie = ?1, club = ?2 where competition = ?3 AND membre = ?4",
membreModel.getCategorie(), membreModel.getClub(), competitionModel,
membreModel)
).toList()).andFailFast());
})
.call(membreModel -> licenceRepository.update("club_id = ?1 where membre = ?2 AND saison = ?3",
(membreModel.getClub() == null) ? null : membreModel.getClub().getId(), membreModel,
Utils.getSaison()))
.call(membreModel -> (membre.getPhoto_data() != null && membre.getPhoto_data()
.size() > 0) ? ls.logAUpdate("Photo",
membreModel) : Uni.createFrom().nullItem())
keycloakService.setClubGroupMembre(membreModel, membreModel.getClub()) : Uni.createFrom().nullItem())
.map(__ -> "OK");
}
public Uni<Long> add(FullMemberForm input) {
return clubRepository.findById(input.getClub())
.call(__ -> repository.count("email LIKE ?1", input.getEmail())
.invoke(Unchecked.consumer(c -> {
if (c > 0 && input.getEmail() != null && !input.getEmail().isBlank())
throw new DBadRequestException(trad.t("email.deja.utilise"));
})))
.chain(clubModel -> {
MembreModel model = getMembreModel(input, clubModel);
return Panache.withTransaction(() -> repository.persist(model));
})
.call(membreModel -> ls.logAAdd(membreModel))
.invoke(membreModel -> SReqComb.sendIfNeedAdd(serverCustom.clients,
SimpleCombModel.fromModel(membreModel)))
.map(MembreModel::getId);
}
public Uni<Long> add(FullMemberForm input, String subject) {
return repository.find("userId = ?1", subject).firstResult()
.call(__ -> repository.count("email LIKE ?1", input.getEmail())
.invoke(Unchecked.consumer(c -> {
if (c > 0) throw new DBadRequestException(trad.t("email.deja.utilise"));
})))
.call(membreModel ->
repository.count(
"unaccent(lname) ILIKE unaccent(?1) AND unaccent(fname) ILIKE unaccent(?2) AND club = ?3",
input.getLname(), input.getFname(), membreModel.getClub())
.invoke(Unchecked.consumer(c -> {
if (c > 0)
throw new DBadRequestException(trad.t("membre.deja.existent"));
})))
.chain(membreModel -> {
MembreModel model = getMembreModel(input, membreModel.getClub());
model.setRole(RoleAsso.MEMBRE);
model.setGrade_arbitrage(GradeArbitrage.NA);
return Panache.withTransaction(() -> repository.persist(model));
})
.call(membreModel -> ls.logAAdd(membreModel))
.invoke(membreModel -> SReqComb.sendIfNeedAdd(serverCustom.clients,
SimpleCombModel.fromModel(membreModel)))
.map(MembreModel::getId);
}
public Uni<String> delete(long id) {
return repository.findById(id)
.call(membreModel -> (membreModel.getUserId() != null) ?
keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem())
.call(membreModel -> ls.logADelete(membreModel))
.call(membreModel -> Panache.withTransaction(() -> repository.delete(membreModel)))
.invoke(membreModel -> SReqComb.sendRm(serverCustom.clients, id))
.map(__ -> "Ok");
}
public Uni<String> delete(long id, SecurityCtx securityCtx) {
return repository.findById(id)
.invoke(Unchecked.consumer(membreModel -> {
if (!securityCtx.isInClubGroup(membreModel.getClub().getId()))
throw new DForbiddenException();
}))
.invoke(Unchecked.consumer(membreModel -> {
if (membreModel.getLicence() != null) {
throw new DBadRequestException(
trad.t("membre.rm.err1"));
}
}))
.call(membreModel -> licenceRepository.find("membre = ?1", membreModel).count()
.invoke(Unchecked.consumer(l -> {
if (l > 0)
throw new DBadRequestException(trad.t("membre.rm.err2"));
})))
.call(membreModel -> (membreModel.getUserId() != null) ?
keycloakService.removeAccount(membreModel.getUserId()) : Uni.createFrom().nullItem())
.call(membreModel -> ls.logADelete(membreModel))
.call(membreModel -> Panache.withTransaction(() -> repository.delete(membreModel)))
.invoke(membreModel -> SReqComb.sendRm(serverCustom.clients, id))
.call(__ -> Utils.deleteMedia(id, media, "ppMembre"))
.map(__ -> "Ok");
}
public Uni<?> setUserId(Long id, String id1) {
return repository.findById(id).chain(membreModel -> {
ls.logChange("KC UUID", membreModel.getUserId(), id1, membreModel);
membreModel.setUserId(id1);
return Panache.withTransaction(() -> repository.persist(membreModel))
.call(() -> ls.append());
return Panache.withTransaction(() -> repository.persist(membreModel));
});
}
private static MembreModel getMembreModel(FullMemberForm input, ClubModel clubModel) {
MembreModel model = new MembreModel();
model.setFname(Utils.formatPrenom(input.getFname()));
model.setLname(input.getLname().toUpperCase());
model.setEmail(input.getEmail());
model.setLicence(null);
model.setGenre(input.getGenre());
model.setCountry(input.getCountry());
model.setBirth_date(input.getBirth_date());
model.setCategorie(Utils.getCategoryFormBirthDate(input.getBirth_date(), new Date()));
model.setClub(clubModel);
model.setRole(input.getRole());
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(m -> meData.setMembre(m, trad))
.call(membreModel -> Mutiny.fetch(membreModel.getLicences())
.map(licences -> licences.stream().map(SimpleLicence::fromModel).toList())
.invoke(meData::setLicences))
.call(membreModel -> Mutiny.fetch(membreModel.getSelections())
.map(licences -> licences.stream().map(SimpleSelection::fromModel).toList())
.invoke(meData::setSelections))
.map(__ -> meData);
}
public Uni<Void> updateSettings(String subject, UserSettingForm settingForm) {
return repository.find("userId = ?1", subject).firstResult()
.invoke(Unchecked.consumer(membreModel -> {
if (settingForm.getResultPrivacy() != null) {
ls.logChange("Confidentialité des résultats", membreModel.getResultPrivacy(),
settingForm.getResultPrivacy(), membreModel);
membreModel.setResultPrivacy(settingForm.getResultPrivacy());
}
}))
.call(membreModel -> Panache.withTransaction(() -> repository.persist(membreModel)))
.call(membreModel -> ls.append())
.replaceWithVoid();
}
@Scheduled(cron = "0 0 1 1 9 ?")
Uni<Void> everySeason() {
return repository.list("birth_date IS NOT NULL")
.chain(l -> Uni.join().all(l.stream().map(m -> {
m.setCategorie(Utils.getCategoryFormBirthDate(m.getBirth_date(), new Date()));
return Panache.withTransaction(() -> repository.persist(m));
}).toList()).andCollectFailures())
.map(__ -> null);
}
}

View File

@@ -1,235 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
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;
import fr.titionfire.ffsaf.rest.exception.DNotFoundException;
import fr.titionfire.ffsaf.utils.Utils;
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 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 org.jboss.logging.Logger;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@WithSession
@ApplicationScoped
public class PDFService {
private static final Logger LOGGER = Logger.getLogger(PDFService.class);
@Inject
CombRepository combRepository;
@Inject
ClubRepository clubRepository;
@ConfigProperty(name = "upload_dir")
String media;
@ConfigProperty(name = "pdf-maker.jar-path")
String pdfMakerJarPath;
@ConfigProperty(name = "pdf-maker.sign-file")
String sign_file;
@Inject
TradService trad;
public Uni<Response> getLicencePdf(String subject) {
return getLicencePdf(combRepository.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(trad.t("pas.de.licence.pour.la.saison.en.cours")));
try {
byte[] buff = make_pdf(m, licence);
if (buff == null)
throw new IOException("Error making pdf");
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 byte[] make_pdf(MembreModel m, LicenceModel licence) throws IOException, InterruptedException {
List<String> cmd = new ArrayList<>();
cmd.add("java");
cmd.add("-jar");
cmd.add(pdfMakerJarPath);
UUID uuid = UUID.randomUUID();
cmd.add("/tmp/" + uuid + ".pdf");
cmd.add("membre");
cmd.add(m.getFname());
cmd.add(m.getLname());
cmd.add(m.getGenre().str);
cmd.add(m.getCategorie().getName());
cmd.add(licence.getCertificate() == null ? "" : licence.getCertificate());
cmd.add(Utils.getSaison() + "");
cmd.add(m.getLicence() + "");
cmd.add(m.getClub().getName());
cmd.add(m.getClub().getNo_affiliation() + "");
cmd.add(m.getBirth_date() == null ? "--" : new SimpleDateFormat("dd/MM/yyyy").format(m.getBirth_date()));
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];
cmd.add(file.getAbsolutePath());
} else {
cmd.add("/dev/null");
}
return getPdf(cmd, uuid);
}
public Uni<Response> getAffiliationPdf(String subject) {
return getAffiliationPdf(
combRepository.find("userId = ?1", subject).firstResult()
.invoke(Unchecked.consumer(m -> {
if (m == null || m.getClub() == null)
throw new DNotFoundException(trad.t("club.non.trouve"));
}))
.map(MembreModel::getClub)
.call(m -> Mutiny.fetch(m.getAffiliations())));
}
public Uni<Response> getAffiliationPdf(long id) {
return getAffiliationPdf(
clubRepository.findById(id)
.invoke(Unchecked.consumer(m -> {
if (m == null)
throw new DNotFoundException(trad.t("club.non.trouve"));
}))
.call(m -> Mutiny.fetch(m.getAffiliations())));
}
private Uni<Response> getAffiliationPdf(Uni<ClubModel> uniBase) {
return uniBase
.map(Unchecked.function(m -> {
if (m.getAffiliations().stream()
.noneMatch(licenceModel -> licenceModel.getSaison() == Utils.getSaison()))
throw new DNotFoundException(trad.t("pas.d.affiliation.pour.la.saison.en.cours"));
try {
byte[] buff = make_pdf(m);
if (buff == null)
throw new IOException("Error making pdf");
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'affiliation " + Utils.getSaison() + "-" +
(Utils.getSaison() + 1) + " de " + m.getName() + ".pdf\"");
return resp.build();
} catch (Exception e) {
throw new IOException(e);
}
}));
}
private byte[] make_pdf(ClubModel m) throws IOException, InterruptedException {
List<String> cmd = new ArrayList<>();
cmd.add("java");
cmd.add("-jar");
cmd.add(pdfMakerJarPath);
UUID uuid = UUID.randomUUID();
cmd.add("/tmp/" + uuid + ".pdf");
cmd.add("club");
cmd.add(m.getName());
cmd.add(Utils.getSaison() + "");
cmd.add(m.getNo_affiliation() + "");
cmd.add(new File(sign_file).getAbsolutePath());
return getPdf(cmd, uuid);
}
static byte[] getPdf(List<String> cmd, UUID uuid) throws IOException, InterruptedException {
ProcessBuilder processBuilder = new ProcessBuilder(cmd);
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
Thread t = new Thread(() -> {
try {
String line;
while ((line = reader.readLine()) != null)
builder.append(line).append("\n");
} catch (Exception ignored) {
}
});
t.start();
int code = -1;
if (!process.waitFor(30, TimeUnit.SECONDS)) {
process.destroy();
builder.append("Timeout...");
} else {
code = process.exitValue();
}
if (t.isAlive())
t.interrupt();
PDFService.LOGGER.debug("PDF maker: " + builder);
if (code != 0) {
throw new IOException("Error code: " + code);
} else {
File file = new File("/tmp/" + uuid + ".pdf");
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buff = fis.readAllBytes();
//noinspection ResultOfMethodCallIgnored
file.delete();
return buff;
} catch (IOException e) {
//noinspection ResultOfMethodCallIgnored
file.delete();
}
return null;
}
}
}

View File

@@ -1,782 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.*;
import fr.titionfire.ffsaf.data.repository.*;
import fr.titionfire.ffsaf.domain.entity.MatchModelExtend;
import fr.titionfire.ffsaf.rest.data.ResultCategoryData;
import fr.titionfire.ffsaf.rest.exception.DBadRequestException;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.utils.*;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.Builder;
import org.hibernate.reactive.mutiny.Mutiny;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
@WithSession
@ApplicationScoped
public class ResultService {
@Inject
CompetitionRepository compRepository;
@Inject
RegisterRepository registerRepository;
@Inject
MembreService membreService;
@Inject
ClubRepository clubRepository;
@Inject
CompetitionGuestRepository competitionGuestRepository;
@Inject
CategoryRepository categoryRepository;
@Inject
MatchRepository matchRepository;
@Inject
CardRepository cardRepository;
@Inject
TradService trad;
private static final HashMap<Long, String> combTempIds = new HashMap<>();
private static String getCombTempId(Long key) {
synchronized (combTempIds) {
if (!combTempIds.containsKey(key)) {
combTempIds.put(key, UUID.randomUUID().toString());
}
return combTempIds.get(key);
}
}
private static Long getCombTempId(String value) {
synchronized (combTempIds) {
for (Map.Entry<Long, String> entry : combTempIds.entrySet()) {
if (entry.getValue().equals(value)) {
return entry.getKey();
}
}
return null;
}
}
private static final HashMap<String, Long> clubTempIds = new HashMap<>();
private static Long getClubTempId(String key) {
synchronized (clubTempIds) {
if (!clubTempIds.containsKey(key)) {
clubTempIds.put(key, (System.currentTimeMillis() + clubTempIds.size()) * -1);
}
return clubTempIds.get(key);
}
}
private static String getClubTempId(Long value) {
synchronized (clubTempIds) {
for (Map.Entry<String, Long> entry : clubTempIds.entrySet()) {
if (entry.getValue().equals(value)) {
return entry.getKey();
}
}
return null;
}
}
public Uni<List<Object[]>> getList(SecurityCtx securityCtx) {
return membreService.getByAccountId(securityCtx.getSubject())
.chain(m -> registerRepository.list(
"membre = ?1 OR (TRUE = ?2 AND membre.club = ?3)",
m, securityCtx.isClubAdmin(), m.getClub()))
.onItem().transformToMulti(Multi.createFrom()::iterable)
.onItem().call(r -> Mutiny.fetch(r.getCompetition()))
.onItem().transform(RegisterModel::getCompetition)
.collect().asList()
.chain(l -> compRepository.list("owner = ?1 OR ?1 IN admin", securityCtx.getSubject())
.map(l2 -> Stream.concat(l.stream(), l2.stream()).distinct()
.map(c -> new Object[]{c.getUuid(), c.getName(), c.getDate()}).toList())
);
}
public Uni<HashMap<String, Long>> getCategoryList(String uuid, SecurityCtx securityCtx) {
return hasAccess(uuid, securityCtx).chain(__ -> getCategoryList(uuid));
}
public Uni<HashMap<String, Long>> getCategoryList(String uuid) {
return categoryRepository.list("compet.uuid = ?1", uuid)
.map(categoryModels -> {
HashMap<String, Long> map = new HashMap<>();
categoryModels.stream()
.sorted(Comparator.comparing(CategoryModel::getName))
.forEachOrdered(categoryModel -> map.put(categoryModel.getName(), categoryModel.getId()));
return map;
});
}
public Uni<ResultCategoryData> getCategory(String uuid, long poule, SecurityCtx securityCtx) {
return hasAccess(uuid, securityCtx).chain(membreModel -> getData(uuid, poule, membreModel));
}
public Uni<ResultCategoryData> getCategory(String uuid, long poule) {
return getData(uuid, poule, null);
}
private Uni<ResultCategoryData> getData(String uuid, long poule, MembreModel membreModel) {
List<CardModel> cards = new ArrayList<>();
return matchRepository.list("category.compet.uuid = ?1 AND category.id = ?2", uuid, poule)
.call(list -> list.isEmpty() ? Uni.createFrom().voidItem() :
Mutiny.fetch(list.get(0).getCategory().getTree()))
.chain(list -> cardRepository.list("competition.uuid = ?1", uuid).invoke(cards::addAll)
.map(c -> list.stream().map(m -> new MatchModelExtend(m, c)).toList()))
.map(matchModels -> {
ResultCategoryData out = new ResultCategoryData();
CategoryModel categoryModel = matchModels.get(0).getCategory();
out.setName(categoryModel.getName());
out.setType(categoryModel.getType());
out.setLiceName(categoryModel.getLiceName() == null ? new String[]{} : categoryModel.getLiceName()
.split(";"));
out.setGenTime(System.currentTimeMillis());
out.setTreeIsClassement(categoryModel.isTreeAreClassement());
getArray2(matchModels, membreModel, out);
getTree(categoryModel.getTree(), membreModel, cards, out);
getClassementArray(categoryModel, membreModel, cards, out);
return out;
});
}
public void getArray2(List<MatchModelExtend> matchModels_, MembreModel membreModel, ResultCategoryData out) {
List<MatchModelExtend> matchModels = matchModels_.stream().filter(o -> o.getCategory_ord() >= 0).toList();
HashMap<Character, List<MatchModelExtend>> matchMap = new HashMap<>();
for (MatchModelExtend model : matchModels) {
char g = model.getPoule();
if (!matchMap.containsKey(g))
matchMap.put(g, new ArrayList<>());
matchMap.get(g).add(model);
}
matchMap.forEach((c, matchEntities) -> {
List<ResultCategoryData.PouleArrayData> matchs = matchEntities.stream()
.sorted(Comparator.comparing(MatchModelExtend::getCategory_ord))
.map(o -> ResultCategoryData.PouleArrayData.fromModel(o, membreModel,
ResultPrivacy.REGISTERED_ONLY_NO_DETAILS))
.toList();
List<ResultCategoryData.RankArray> rankArray = matchEntities.stream()
.flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id(), m.getC1_guest(), m.getC2_guest()))
.distinct()
.filter(Objects::nonNull)
.map(comb -> {
CombStat stat = makeStat(matchEntities, comb);
return new ResultCategoryData.RankArray(0, comb,
comb.getName(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS), stat.score, stat.w,
stat.pointMake, stat.pointTake, stat.getPointRate());
})
.sorted(Comparator
.comparing(ResultCategoryData.RankArray::getScore)
.thenComparing(ResultCategoryData.RankArray::getWin)
.thenComparing(ResultCategoryData.RankArray::getPointRate).reversed())
.toList();
out.getMatchs().put(c, matchs);
int lastScore = -1;
int lastWin = -1;
float pointRate = 0;
int rank = 0;
for (ResultCategoryData.RankArray rankArray1 : rankArray) {
if (rankArray1.getScore() != lastScore || rankArray1.getWin() != lastWin || pointRate != rankArray1.getPointRate()) {
lastScore = rankArray1.getScore();
lastWin = rankArray1.getWin();
pointRate = rankArray1.getPointRate();
rank++;
}
rankArray1.setRank(rank);
}
out.getRankArray().put(c, rankArray);
});
}
private void getClassementArray(CategoryModel categoryModel, MembreModel membreModel, List<CardModel> cards,
ResultCategoryData out) {
if ((categoryModel.getType() & 2) != 0) {
AtomicInteger rank = new AtomicInteger(0);
categoryModel.getTree().stream()
.filter(t -> t.getLevel() > 0)
.sorted(Comparator.comparing(TreeModel::getLevel))
.forEach(treeModel -> makeClassementRow(membreModel,
new MatchModelExtend(treeModel.getMatch(), cards), out, rank));
categoryModel.getTree().stream()
.filter(t -> t.getLevel() <= -10)
.sorted(Comparator.comparing(TreeModel::getLevel).reversed())
.forEach(treeModel -> makeClassementRow(membreModel,
new MatchModelExtend(treeModel.getMatch(), cards), out, rank));
} else {
for (List<ResultCategoryData.RankArray> list : out.getRankArray().values()) {
for (ResultCategoryData.RankArray r : list) {
out.getClassement().add(new ResultCategoryData.ClassementData(r.getRank(), r.getComb(), r.getName()));
}
}
}
}
private static void makeClassementRow(MembreModel membreModel, MatchModelExtend m, ResultCategoryData out,
AtomicInteger rank) {
if (m.isEnd()) {
if (m.getWin() > 0) {
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC1(),
m.getC1Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC2(),
m.getC2Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
} else if (m.getWin() < 0) {
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC2(),
m.getC2Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC1(),
m.getC1Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
} else {
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC1(),
m.getC1Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
out.getClassement()
.add(new ResultCategoryData.ClassementData(rank.getAndIncrement(), m.getC2(),
m.getC2Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
}
} else {
out.getClassement().add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), null, null));
out.getClassement().add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), null, null));
}
}
private static void convertTree(TreeModel src, TreeNode<ResultCategoryData.TreeData> dst, MembreModel membreModel,
ResultPrivacy privacy, List<CardModel> cards) {
dst.setData(
ResultCategoryData.TreeData.from(new MatchModelExtend(src.getMatch(), cards), src.getLevel(),
membreModel, privacy));
if (src.getLeft() != null) {
dst.setLeft(new TreeNode<>());
convertTree(src.getLeft(), dst.getLeft(), membreModel, privacy, cards);
}
if (src.getRight() != null) {
dst.setRight(new TreeNode<>());
convertTree(src.getRight(), dst.getRight(), membreModel, privacy, cards);
}
}
private void getTree(List<TreeModel> treeModels, MembreModel membreModel, List<CardModel> cards,
ResultCategoryData out) {
ArrayList<TreeNode<ResultCategoryData.TreeData>> trees = new ArrayList<>();
treeModels.stream()
.filter(t -> t.getLevel() != 0)
.sorted(Comparator.comparing(TreeModel::getLevel))
.forEach(treeModel -> {
TreeNode<ResultCategoryData.TreeData> root = new TreeNode<>();
convertTree(treeModel, root, membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS, cards);
trees.add(root);
});
out.setTrees(trees);
}
public Uni<CombsArrayData> getAllCombArray(String uuid, SecurityCtx securityCtx) {
return hasAccess(uuid, securityCtx)
.chain(membreModel -> getAllCombArray_(uuid, membreModel));
}
public Uni<CombsArrayData> getAllCombArrayPublic(String uuid) {
return getAllCombArray_(uuid, null);
}
private Uni<CombsArrayData> getAllCombArray_(String uuid, MembreModel membreModel) {
return registerRepository.list("competition.uuid = ?1", uuid)
.chain(registers -> matchRepository.list("category.compet.uuid = ?1", uuid)
.chain(matchModels -> cardRepository.list("competition.uuid = ?1", uuid)
.map(cards -> new Pair<>(registers,
matchModels.stream().map(m -> new MatchModelExtend(m, cards)).toList()))))
.map(pair -> {
List<RegisterModel> registers = pair.getKey();
List<MatchModelExtend> matchModels = pair.getValue();
CombsArrayData.CombsArrayDataBuilder builder = CombsArrayData.builder();
List<CombsArrayData.CombsData> combs = matchModels.stream()
.flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id(), m.getC1_guest(), m.getC2_guest()))
.filter(Objects::nonNull)
.distinct()
.map(comb -> {
var builder2 = CombsArrayData.CombsData.builder();
CombStat stat = makeStat(matchModels, comb);
Categorie categorie = null;
String clubName = null;
Optional<RegisterModel> register = registers.stream()
.filter(r -> Objects.equals(r.getMembre(), comb)).findFirst();
if (register.isPresent()) {
categorie = register.get().getCategorie2();
ClubModel club = register.get().getClub2();
clubName = (club == null) ? trad.t("no.licence") : club.getName();
} else if (comb instanceof CompetitionGuestModel guestModel) {
categorie = guestModel.getCategorie();
clubName = guestModel.getClub();
} else if (comb instanceof MembreModel model) {
categorie = model.getCategorie();
clubName = (model.getClub() == null) ? trad.t("no.licence")
: model.getClub().getName();
}
builder2.cat((categorie == null) ? "---" : categorie.getName(trad));
builder2.name(comb.getName(membreModel, ResultPrivacy.REGISTERED_ONLY));
builder2.w(stat.w);
builder2.l(stat.l);
builder2.ratioVictoire((stat.l == 0) ? stat.w : (float) stat.w / stat.l);
builder2.club(clubName);
builder2.pointMake(stat.pointMake);
builder2.pointTake(stat.pointTake);
builder2.ratioPoint(stat.getPointRate());
return builder2.build();
})
.sorted(Comparator.comparing(CombsArrayData.CombsData::name))
.toList();
builder.nb_insc(combs.size());
builder.tt_match((int) matchModels.stream().filter(MatchModelExtend::isEnd).count());
builder.point(matchModels.stream()
.filter(MatchModelExtend::isEnd)
.flatMap(m -> m.getScoresToCompute().stream())
.mapToInt(s -> s.getS1() + s.getS2()).sum());
builder.combs(combs);
return builder.build();
});
}
public Uni<HashMap<String, String>> getCombList(String uuid, SecurityCtx securityCtx) {
return hasAccess(uuid, securityCtx)
.chain(membreModel -> getCombList(uuid, ResultPrivacy.REGISTERED_ONLY));
}
public Uni<HashMap<String, String>> getCombList(String uuid) {
return getCombList(uuid, ResultPrivacy.PUBLIC);
}
private Uni<HashMap<String, String>> getCombList(String uuid, ResultPrivacy privacy) {
return registerRepository.list("competition.uuid = ?1 AND membre.resultPrivacy <= ?2", uuid, privacy)
.map(models -> {
HashMap<String, String> map = new HashMap<>();
models.forEach(
r -> map.put(r.getMembre().getName(), getCombTempId(r.getMembre().getId())));
return map;
})
.chain(map -> competitionGuestRepository.list("competition.uuid = ?1 AND lname != \"__team\"", uuid)
.map(models -> {
models.forEach(guestModel -> map.put(guestModel.getName(),
getCombTempId(guestModel.getId() * -1)));
return map;
})
);
}
public Uni<?> getCombArrayPublic(String uuid, String combTempId, SecurityCtx securityCtx) {
return hasAccess(uuid, securityCtx)
.chain(membreModel -> getCombArrayPublic(uuid, combTempId, ResultPrivacy.REGISTERED_ONLY));
}
public Uni<?> getCombArrayPublic(String uuid, String combTempId) {
return getCombArrayPublic(uuid, combTempId, ResultPrivacy.PUBLIC);
}
private Uni<?> getCombArrayPublic(String uuid, String combTempId, ResultPrivacy privacy) {
CombArrayData.CombArrayDataBuilder builder = CombArrayData.builder();
Long id = getCombTempId(combTempId);
if (id == null) {
return Uni.createFrom().failure(new DForbiddenException(trad.t("comb.not.found")));
}
Uni<List<MatchModelExtend>> uni;
if (id >= 0) {
uni = registerRepository.find("membre.id = ?1 AND competition.uuid = ?2 AND membre.resultPrivacy <= ?3", id,
uuid, privacy).firstResult()
.chain(Unchecked.function(registerModel -> {
if (registerModel == null)
throw new DBadRequestException(trad.t("combattant.non.inscrit"));
builder.name(Utils.getFullName(registerModel.getMembre()));
builder.club((registerModel.getClub2() == null) ? trad.t("no.licence") :
registerModel.getClub2().getName());
builder.cat((registerModel.getCategorie2() == null) ? "---" :
registerModel.getCategorie2().getName(trad));
return matchRepository.list(
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.comb c1g LEFT JOIN m.c2_guest.comb c2g " +
"WHERE m.category.compet.uuid = ?1 AND (m.c1_id = ?2 OR m.c2_id = ?2 OR c1g = ?2 OR c2g = ?2)",
uuid, registerModel.getMembre())
.chain(matchModels -> cardRepository.list("competition.uuid = ?1", uuid)
.map(cards -> matchModels.stream().map(m -> new MatchModelExtend(m, cards))
.toList()));
}));
} else {
uni = competitionGuestRepository.find("id = ?1 AND competition.uuid = ?2", -id, uuid).firstResult()
.chain(guestModel -> {
builder.name(Utils.getFullName(guestModel));
builder.club(guestModel.getClub());
builder.cat(
(guestModel.getCategorie() == null) ? "---" : guestModel.getCategorie().getName(trad));
return matchRepository.list(
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.guest c1g LEFT JOIN m.c2_guest.guest c2g " +
"WHERE m.category.compet.uuid = ?1 AND (m.c1_guest = ?2 OR m.c2_guest = ?2 OR c1g = ?2 OR c2g = ?2)",
uuid, guestModel)
.chain(matchModels -> cardRepository.list("competition.uuid = ?1", uuid)
.map(cards -> matchModels.stream().map(m -> new MatchModelExtend(m, cards))
.toList()));
});
}
return uni.invoke(matchModels -> {
List<CategoryModel> pouleModels = matchModels.stream().map(MatchModelExtend::getCategory).distinct()
.toList();
List<CombArrayData.MatchsData> matchs = new ArrayList<>();
AtomicInteger sumW = new AtomicInteger();
AtomicInteger sumPointMake = new AtomicInteger(0);
AtomicInteger sumPointTake = new AtomicInteger(0);
for (MatchModelExtend matchModel : matchModels) {
if ((matchModel.getC1_id() == null && matchModel.getC1_guest() == null) ||
(matchModel.getC2_id() == null && matchModel.getC2_guest() == null))
continue;
var builder2 = CombArrayData.MatchsData.builder();
builder2.date(matchModel.getDate());
builder2.poule(pouleModels.stream().filter(p -> p.equals(matchModel.getCategory()))
.map(CategoryModel::getName).findFirst().orElse(""));
builder2.end(matchModel.isEnd());
AtomicInteger pointMake = new AtomicInteger();
AtomicInteger pointTake = new AtomicInteger();
if (matchModel.isC1(id)) {
builder2.adv(matchModel.getC2Name());
if (matchModel.isEnd()) {
matchModel.getScoresToCompute()
.forEach(scoreEntity -> {
pointMake.addAndGet(scoreEntity.getS1());
pointTake.addAndGet(scoreEntity.getS2());
});
builder2.score(matchModel.getScoresToPrint().stream()
.map(s -> new Integer[]{s.getS1(), s.getS2()}).toList());
} else {
builder2.score(new ArrayList<>());
}
builder2.win(matchModel.isEnd() && matchModel.getWin() > 0);
} else {
builder2.adv(matchModel.getC1Name());
if (matchModel.isEnd()) {
matchModel.getScoresToCompute()
.forEach(scoreEntity -> {
pointMake.addAndGet(scoreEntity.getS2());
pointTake.addAndGet(scoreEntity.getS1());
});
builder2.score(matchModel.getScoresToPrint().stream()
.map(s -> new Integer[]{s.getS2(), s.getS1()}).toList());
} else {
builder2.score(new ArrayList<>());
}
builder2.win(matchModel.isEnd() && matchModel.getWin() < 0);
}
builder2.eq(matchModel.isEnd() && matchModel.getWin() == 0);
builder2.ratio(
(pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get());
sumPointMake.addAndGet(pointMake.get());
sumPointTake.addAndGet(pointTake.get());
matchs.add(builder2.build());
if (builder2.win)
sumW.getAndIncrement();
}
builder.totalWin(sumW.get());
builder.pointRatio(
(sumPointTake.get() == 0) ? sumPointMake.get() : (float) sumPointMake.get() / sumPointTake.get());
builder.pointMake(sumPointMake.get());
builder.pointTake(sumPointTake.get());
matchs.sort(Comparator.comparing(CombArrayData.MatchsData::poule)
.thenComparing(CombArrayData.MatchsData::adv));
builder.matchs(matchs);
})
.map(__ -> builder.build());
}
@Builder
@RegisterForReflection
public static record CombsArrayData(int nb_insc, int tt_match, long point, List<CombsData> combs) {
@Builder
@RegisterForReflection
public static record CombsData(String cat, String club, String name, int w, int l, float ratioVictoire,
float ratioPoint, int pointMake, int pointTake) {
}
}
@Builder
@RegisterForReflection
public static record CombArrayData(String name, String club, String cat, int totalWin,
float pointRatio, int pointMake, int pointTake, List<MatchsData> matchs) {
@Builder
@RegisterForReflection
public static record MatchsData(Date date, String poule, String adv, List<Integer[]> score, float ratio,
boolean win, boolean eq, boolean end) {
}
}
public Uni<HashMap<String, Long>> getClubList(String uuid, SecurityCtx securityCtx) {
return hasAccess(uuid, securityCtx).chain(__ -> getClubList(uuid));
}
public Uni<HashMap<String, Long>> getClubList(String uuid) {
return registerRepository.list("competition.uuid = ?1", uuid)
.map(registers -> {
HashMap<String, Long> registerMap = new HashMap<>();
registers.stream().map(RegisterModel::getClub2).distinct().filter(Objects::nonNull)
.forEach(registerClub -> registerMap.put(registerClub.getName(), registerClub.getId()));
return registerMap;
})
.chain(map -> competitionGuestRepository.list("competition.uuid = ?1", uuid)
.map(guests -> {
guests.stream().map(CompetitionGuestModel::getClub).distinct()
.filter(Objects::nonNull)
.forEach(guestClub -> map.putIfAbsent(guestClub,
getClubTempId(guestClub)));
return map;
})
);
}
public Uni<ClubArrayData> getClubArray(String uuid, Long id, SecurityCtx securityCtx) {
return hasAccess(uuid, securityCtx).chain(membreModel -> getClubArray2(uuid, id, membreModel));
}
public Uni<ClubArrayData> getClubArray2(String uuid, Long id, MembreModel membreModel) {
if (id < 0) {
String clubName = getClubTempId(id);
if (clubName == null) {
return Uni.createFrom().failure(new DForbiddenException(trad.t("club.not.found")));
}
return competitionGuestRepository.list("competition.uuid = ?1 AND club = ?2", uuid, clubName)
.call(list -> {
if (list.isEmpty())
return Uni.createFrom().failure(new DBadRequestException(trad.t("club.not.found")));
return Uni.createFrom().voidItem();
})
.chain(guests -> matchRepository.list(
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.guest c1g LEFT JOIN m.c2_guest.guest c2g " +
"WHERE m.category.compet.uuid = ?1 AND (m.c1_guest IN ?2 OR m.c2_guest IN ?2 OR c1g IN ?2 OR c2g IN ?2)",
uuid, guests)
.chain(mm -> cardRepository.list("competition.uuid = ?1", uuid)
.map(cards ->
getClubArray2(clubName, guests.stream().map(o -> (CombModel) o).toList(),
mm.stream().map(m -> new MatchModelExtend(m, cards)).toList(),
new ArrayList<>(), membreModel)
)));
} else {
return clubRepository.findById(id).chain(clubModel ->
registerRepository.list("competition.uuid = ?1 AND membre.club = ?2", uuid, clubModel)
.chain(registers -> matchRepository.list(
"SELECT DISTINCT m FROM MatchModel m LEFT JOIN m.c1_guest.comb c1g LEFT JOIN m.c2_guest.comb c2g " +
"WHERE m.category.compet.uuid = ?1 AND (m.c1_id IN ?2 OR m.c2_id IN ?2 OR c1g IN ?2 OR c2g IN ?2)",
uuid, registers.stream().map(RegisterModel::getMembre).toList())
.chain(matchModels -> cardRepository.list("competition.uuid = ?1", uuid)
.map(cards ->
getClubArray2(clubModel.getName(),
registers.stream().map(o -> (CombModel) o.getMembre())
.toList(),
matchModels.stream()
.map(m -> new MatchModelExtend(m, cards)).toList(),
registers, membreModel)
))));
}
}
private ClubArrayData getClubArray2(String name, List<CombModel> combs, List<MatchModelExtend> matchModels,
List<RegisterModel> registers, MembreModel membreModel) {
ClubArrayData.ClubArrayDataBuilder builder = ClubArrayData.builder();
builder.name(name);
builder.nb_insc(combs.size());
ArrayList<Long> win_ids = new ArrayList<>();
ArrayList<Long> match_ids = new ArrayList<>();
List<ClubArrayData.CombData> combData = combs.stream().map(comb -> {
var builder2 = ClubArrayData.CombData.builder();
CombStat stat = makeStat(matchModels, comb);
Categorie categorie = null;
Optional<RegisterModel> register = registers.stream()
.filter(r -> Objects.equals(r.getMembre(), comb)).findFirst();
if (register.isPresent()) {
categorie = register.get().getCategorie2();
} else if (comb instanceof CompetitionGuestModel guestModel) {
categorie = guestModel.getCategorie();
} else if (comb instanceof MembreModel model) {
categorie = model.getCategorie();
}
builder2.cat((categorie == null) ? "---" : categorie.getName(trad));
builder2.name(comb.getName(membreModel, ResultPrivacy.REGISTERED_ONLY));
builder2.w(stat.w);
builder2.l(stat.l);
builder2.ratioVictoire((stat.l == 0) ? stat.w : (float) stat.w / stat.l);
builder2.pointMake(stat.pointMake);
builder2.pointTake(stat.pointTake);
builder2.ratioPoint(stat.getPointRate());
win_ids.addAll(stat.win_ids);
match_ids.addAll(stat.match_ids);
return builder2.build();
})
.sorted(Comparator.comparing(ClubArrayData.CombData::name))
.toList();
builder.nb_match((int) match_ids.stream().distinct().count());
builder.match_w((int) win_ids.stream().distinct().count());
builder.ratioVictoire((float) combData.stream().filter(c -> c.l + c.w != 0)
.mapToDouble(ClubArrayData.CombData::ratioVictoire).average().orElse(0L));
builder.pointMake(combData.stream().mapToInt(ClubArrayData.CombData::pointMake).sum());
builder.pointTake(combData.stream().mapToInt(ClubArrayData.CombData::pointTake).sum());
builder.ratioPoint((float) combData.stream().filter(c -> c.l + c.w != 0)
.mapToDouble(ClubArrayData.CombData::ratioPoint).average().orElse(0L));
builder.combs(combData);
return builder.build();
}
private static CombStat makeStat(List<MatchModelExtend> matchModels, CombModel comb) {
CombStat stat = new CombStat();
matchModels.stream()
.filter(m -> m.isEnd() && (m.isC1(comb) || m.isC2(comb)))
.forEach(matchModel -> {
stat.match_ids.add(matchModel.getId());
int win = matchModel.getWin();
if (win == 0) {
stat.score += 1;
} else if ((matchModel.isC1(comb) && win > 0) || matchModel.isC2(comb) && win < 0) {
stat.w++;
stat.win_ids.add(matchModel.getId());
stat.score += 2;
} else {
stat.l++;
}
matchModel.getScoresToCompute()
.forEach(score -> {
if (matchModel.isC1(comb)) {
stat.pointMake += score.getS1();
stat.pointTake += score.getS2();
} else {
stat.pointMake += score.getS2();
stat.pointTake += score.getS1();
}
});
});
return stat;
}
@Builder
@RegisterForReflection
public static record ClubArrayData(String name, int nb_insc, int nb_match, int match_w, float ratioVictoire,
float ratioPoint, int pointMake, int pointTake, List<CombData> combs) {
@Builder
@RegisterForReflection
public static record CombData(String cat, String name, int w, int l, float ratioVictoire,
float ratioPoint, int pointMake, int pointTake) {
}
}
@RegisterForReflection
public static class CombStat {
public int w;
public int l;
public int score;
public int pointMake;
public int pointTake;
public ArrayList<Long> win_ids = new ArrayList<>();
public ArrayList<Long> match_ids = new ArrayList<>();
public CombStat() {
this.w = 0;
this.l = 0;
this.score = 0;
this.pointMake = 0;
this.pointTake = 0;
}
public float getPointRate() {
return (pointTake == 0) ? pointMake : (float) pointMake / pointTake;
}
}
private Uni<MembreModel> hasAccess(String uuid, SecurityCtx securityCtx) {
return registerRepository.find("membre.userId = ?1 AND competition.uuid = ?2", securityCtx.getSubject(), uuid)
.firstResult()
.chain(Unchecked.function(o -> {
if (o != null)
return Uni.createFrom().item(o.getMembre());
return membreService.getByAccountId(securityCtx.getSubject()).chain(m -> {
if (securityCtx.isClubAdmin()) {
return registerRepository.count("membre.club = ?2 AND competition.uuid = ?1",
uuid, m.getClub()).chain(c -> {
if (c > 0) return Uni.createFrom().item(m);
return compRepository.count("uuid = ?1 AND (owner = ?2 OR ?2 IN admin)",
uuid, securityCtx.getSubject())
.chain(c2 -> {
if (c2 > 0) return Uni.createFrom().item(m);
return Uni.createFrom().failure(new DForbiddenException(
trad.t("access.denied")));
});
});
} else {
return compRepository.count("uuid = ?1 AND (owner = ?2 OR ?2 IN admin)", uuid,
securityCtx.getSubject())
.chain(c2 -> {
if (c2 > 0) return Uni.createFrom().item(m);
return Uni.createFrom()
.failure(new DForbiddenException(trad.t("access.denied")));
});
}
});
}));
}
}

View File

@@ -1,64 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.model.LogModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.model.SelectionModel;
import fr.titionfire.ffsaf.data.repository.CombRepository;
import fr.titionfire.ffsaf.data.repository.SelectionRepository;
import fr.titionfire.ffsaf.rest.data.SimpleSelection;
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.List;
import java.util.function.Consumer;
@WithSession
@ApplicationScoped
public class SelectionService {
@Inject
CombRepository combRepository;
@Inject
SelectionRepository repository;
@Inject
LoggerService ls;
public Uni<List<SelectionModel>> getSelection(long id, Consumer<MembreModel> checkPerm) {
return combRepository.findById(id).invoke(checkPerm)
.chain(combRepository -> Mutiny.fetch(combRepository.getSelections()));
}
public Uni<SelectionModel> setSelection(long id, SimpleSelection data) {
if (data.getId() == -1) {
return combRepository.findById(id).chain(membreModel -> {
SelectionModel model = new SelectionModel();
model.setMembre(membreModel);
model.setSaison(data.getSaison());
model.setCategorie(data.getCategorie());
return Panache.withTransaction(() -> repository.persist(model))
.call(licenceModel -> ls.logA(LogModel.ActionType.ADD, membreModel.getObjectName(),
licenceModel));
});
} else {
return repository.findById(data.getId()).chain(model -> {
ls.logChange("Catégorie", model.getCategorie(), data.getCategorie(), model);
model.setCategorie(data.getCategorie());
return Panache.withTransaction(() -> repository.persist(model))
.call(__ -> ls.append());
});
}
}
public Uni<Void> deleteSelection(long id) {
return repository.findById(id)
.call(model -> ls.logADelete(model))
.chain(model -> Panache.withTransaction(() -> repository.delete(model)));
}
}

View File

@@ -1,71 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.data.repository.LicenceRepository;
import fr.titionfire.ffsaf.rest.data.LicenceStats;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.hibernate.reactive.mutiny.Mutiny;
import java.util.concurrent.ConcurrentHashMap;
@WithSession
@ApplicationScoped
public class StatsService {
@Inject
LicenceRepository licenceRepository;
public Uni<LicenceStats> getStats() {
ConcurrentHashMap<Categorie, Integer> categories = new ConcurrentHashMap<>();
LicenceStats stats = new LicenceStats();
int currentSaison = Utils.getSaison();
//noinspection ReactiveStreamsUnusedPublisher
return licenceRepository.listAll()
.onItem().transformToMulti(licences -> Multi.createFrom().iterable(licences))
.call(licence -> Mutiny.fetch(licence.getMembre()))
.invoke(licence -> {
if (!stats.getLicences().containsKey(licence.getSaison()))
stats.getLicences().put(licence.getSaison(), new LicenceStats.YearStats());
LicenceStats.YearStats yearStats = stats.getLicences().get(licence.getSaison());
// System.out.println("stats: " + licence.getMembre().getFname());
if (licence.isValidate()) {
if (licence.getMembre().getGenre() == Genre.H)
yearStats.addH();
else if (licence.getMembre().getGenre() == Genre.F)
yearStats.addF();
else
yearStats.addNa();
if (licence.getSaison() == currentSaison && licence.getMembre().getCategorie() != null) {
categories.put(licence.getMembre().getCategorie(),
categories.getOrDefault(licence.getMembre().getCategorie(), 0) + 1);
}
} else {
yearStats.addNotValid();
}
})
.collect().asList()
.map(__ -> {
for (Categorie c : Categorie.values()) {
LicenceStats.CategoriesStats o = new LicenceStats.CategoriesStats();
o.setName(c.getName());
o.setCount(categories.getOrDefault(c, 0));
stats.getCategories().add(o);
}
return stats;
});
}
}

View File

@@ -1,52 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import io.quarkus.websockets.next.WebSocketConnection;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;
import jakarta.ws.rs.container.ContainerRequestContext;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
@RequestScoped
public class TradService {
@Inject
Instance<ContainerRequestContext> requestContextInstance;
public static final Locale fallbackLocale = Locale.FRANCE;
public String t(String key) {
return translate(key);
}
public String t(String key, WebSocketConnection connection) {
List<String> lang = connection.handshakeRequest().headers().get("Accept-Language");
Locale userLocale = lang != null && !lang.isEmpty() ? Locale.forLanguageTag(lang.get(0)) : fallbackLocale;
return translate(key, userLocale);
}
public String translate(String key) {
ContainerRequestContext requestContext = requestContextInstance.get();
Locale userLocale = (Locale) requestContext.getProperty("userLocale");
return translate(key, userLocale);
}
public String translate(String key, Locale userLocale) {
try {
ResourceBundle messages = ResourceBundle.getBundle("lang.messages", userLocale);
return messages.getString(key);
} catch (MissingResourceException e) {
try {
ResourceBundle fallbackMessages = ResourceBundle.getBundle("lang.messages", fallbackLocale);
return fallbackMessages.getString(key);
} catch (MissingResourceException ex) {
return key;
}
}
}
}

View File

@@ -1,23 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.HashMap;
@ApplicationScoped
public class UpdateService { // For public result page
static HashMap<Long, Long> lastUpdate = new HashMap<>();
public void setNewData(long id) {
lastUpdate.put(id, System.currentTimeMillis());
}
public boolean needUpdate(long id, long last_update) {
if (!lastUpdate.containsKey(id)) {
lastUpdate.put(id, System.currentTimeMillis() - 5000);
return true;
}
return lastUpdate.getOrDefault(id, 0L) > last_update;
}
}

View File

@@ -1,28 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import io.quarkiverse.antivirus.runtime.Antivirus;
import io.quarkiverse.antivirus.runtime.AntivirusScanResult;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.io.InputStream;
import java.util.List;
@ApplicationScoped
public class VirusScannerService {
@Inject
Antivirus antivirus;
public Uni<List<AntivirusScanResult>> scanFileReactive(String fileName, InputStream inputStream) {
System.out.println("Starting reactive virus scan for file: " + fileName);
// Wrap the blocking antivirus scan in a reactive context
// This moves the blocking operation to a worker thread
return Uni.createFrom().item(() -> {
System.out.println("Scanning file on worker thread: " + fileName);
return antivirus.scan(fileName, inputStream);
}).runSubscriptionOn(io.smallrye.mutiny.infrastructure.Infrastructure.getDefaultWorkerPool());
}
}

View File

@@ -1,40 +0,0 @@
package fr.titionfire.ffsaf.domain.service;
import fr.titionfire.ffsaf.rest.client.dto.HelloassoNotification;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
@ApplicationScoped
public class WebhookService {
@Inject
CheckoutService checkoutService;
@Inject
CompetitionService competitionService;
@ConfigProperty(name = "helloasso.organizationSlug")
String organizationSlug;
public Uni<Response> helloAssoNotification(HelloassoNotification notification) {
if (notification.getEventType().equals("Payment")) {
if (notification.getData().getOrder().getFormType().equals("Checkout")) {
if (notification.getData().getOrder().getOrganizationSlug().equalsIgnoreCase(organizationSlug)) {
return checkoutService.paymentStatusChange(notification.getData().getState(),
notification.getMetadata());
}
} else if (notification.getData().getOrder().getFormType().equals("Event")) {
return competitionService.unregisterHelloAsso(notification.getData());
}
}else if (notification.getEventType().equals("Order")){
if (notification.getData().getFormType().equals("Event")) {
return competitionService.registerHelloAsso(notification.getData());
}
}
return Uni.createFrom().item(Response.ok().build());
}
}

View File

@@ -40,7 +40,7 @@ public class Client_Thread extends Thread {
private boolean isAuth;
private final HashMap<UUID, JsonConsumer<?>> waitResult = new HashMap<>();
private final HashMap<UUID, JsonConsumer<Object>> 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<?> consumer) {
public void sendReq(Object object, String code, JsonConsumer<Object> consumer) {
UUID uuid;
do {
uuid = UUID.randomUUID();

View File

@@ -22,7 +22,6 @@ public class SimpleClubModel {
if (model == null)
return null;
return new SimpleClubModel(model.getId(), model.getName(), model.getCountry(),
"/api/club/" + model.getClubId() + "/logo");
return new SimpleClubModel(model.getId(), model.getName(), model.getCountry(), model.getShieldURL());
}
}

View File

@@ -8,14 +8,12 @@ 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 = "";
@@ -32,6 +30,6 @@ public class SimpleCombModel {
return new SimpleCombModel(model.getId(), model.getLname(), model.getFname(), model.getCategorie(),
(model.getClub() == null) ? null : SimpleClubModel.fromModel(model.getClub()),
model.getGenre(), (model.getLicence() == null) ? -1 : model.getLicence(), model.getCountry());
model.getGenre(), model.getLicence(), model.getCountry());
}
}

View File

@@ -1,10 +0,0 @@
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) {
}

View File

@@ -11,6 +11,16 @@ import java.util.HashMap;
public class RComb {
private static final Logger LOGGER = Logger.getLogger(RComb.class);
final IAction findComb = (client_Thread, message) -> {
try {
SimpleCombModel combModel = ServerCustom.getInstance().membreService.find(message.data().get("licence").asInt(), message.data().get("np").asText());
client_Thread.sendRepTo(combModel, message);
} catch (Throwable e) {
LOGGER.error(e.getMessage(), e);
client_Thread.sendErrTo(e.getMessage(), message);
}
};
final CIA<Long> findByIdOptionalComb = new CIA<>(Long.class, (client_Thread, message) -> {
try {
SimpleCombModel combModel = ServerCustom.getInstance().membreService.findByIdOptionalComb(message.data());
@@ -24,6 +34,7 @@ public class RComb {
public static void register(HashMap<String, IAction> iMap) {
RComb rComb = new RComb();
iMap.put("findComb", rComb.findComb);
iMap.put("findByIdOptionalComb", rComb.findByIdOptionalComb);
}
}

View File

@@ -1,40 +0,0 @@
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<String, 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");
}
}
}

View File

@@ -1,29 +0,0 @@
package fr.titionfire.ffsaf.net2.request;
import fr.titionfire.ffsaf.net2.Client_Thread;
import fr.titionfire.ffsaf.rest.data.CompetitionData;
import java.util.ArrayList;
import java.util.HashMap;
public class SReqRegister {
public static void sendIfNeed(ArrayList<Client_Thread> client_Thread, CompetitionData.SimpleRegister simpleRegister, Long competitionId) {
HashMap<String, Object> map = new HashMap<>();
map.put("simpleRegister", simpleRegister);
map.put("competitionId", competitionId);
for (Client_Thread client : client_Thread) {
client.sendNotify(map, "sendRegister");
}
}
public static void sendRmIfNeed(ArrayList<Client_Thread> clients, Long combId, Long id) {
HashMap<String, Object> map = new HashMap<>();
map.put("combId", combId);
map.put("competitionId", id);
for (Client_Thread client : clients) {
client.sendNotify(map, "sendRmRegister");
}
}
}

View File

@@ -1,94 +0,0 @@
package fr.titionfire.ffsaf.rest;
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_tresorier", "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("{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);
}
@DELETE
@Path("{id}")
@RolesAllowed("federation_admin")
@Produces(MediaType.TEXT_PLAIN)
@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);
}
}

View File

@@ -1,191 +0,0 @@
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_tresorier", "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", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@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, @QueryParam("reason") String reason) {
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, reason, securityCtx.roleHas("federation_admin")));
}
@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_tresorier", "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,
Uni.createFrom().nullItem(), false);
}
}

View File

@@ -1,43 +0,0 @@
package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.rest.client.SirenService;
import fr.titionfire.ffsaf.rest.client.StateIdService;
import fr.titionfire.ffsaf.rest.data.AssoData;
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 org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.rest.client.inject.RestClient;
@Path("api/asso")
public class AssoEndpoints {
@RestClient
StateIdService stateIdService;
@RestClient
SirenService sirenService;
@Inject
TradService trad;
@GET
@Path("state_id/{stateId}")
@Produces(MediaType.APPLICATION_JSON)
@Operation(hidden = true)
public Uni<AssoData> getAssoInfo(@PathParam("stateId") String stateId) {
return ((stateId.charAt(0) == 'W') ? stateIdService.get_rna(stateId) : sirenService.get_unite(
stateId).chain(stateIdService::getAssoDataFromUnit)).onFailure().transform(throwable -> {
if (throwable instanceof WebApplicationException exception) {
if (exception.getResponse().getStatus() == 404)
return new DNotFoundException(trad.t("service.momentanement.indisponible"));
if (exception.getResponse().getStatus() == 400)
return new DNotFoundException(trad.t("asso.introuvable"));
}
return throwable;
});
}
}

View File

@@ -11,9 +11,6 @@ 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;
@@ -28,16 +25,10 @@ public class AuthEndpoints {
SecurityIdentity securityIdentity;
@Inject
JsonWebToken IdToken;
JsonWebToken accessToken;
@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();
}
@@ -46,21 +37,14 @@ 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(IdToken, securityIdentity);
return UserInfo.makeUserInfo(accessToken, securityIdentity);
}
@GET
@Path("/login")
@Authenticated
@Produces(MediaType.TEXT_PLAIN)
@Operation(hidden = true)
public Response login() throws URISyntaxException {
return Response.temporaryRedirect(new URI(redirect)).build();
}

View File

@@ -1,63 +0,0 @@
package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.CategoryService;
import fr.titionfire.ffsaf.rest.data.CategoryData;
import fr.titionfire.ffsaf.rest.data.CategoryFullData;
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}/admin/")
public class CategoryAdminEndpoints {
@PathParam("system")
private CompetitionSystem system;
@Inject
CategoryService service;
@Inject
SecurityCtx securityCtx;
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Uni<CategoryData> getByIdAdmin(@PathParam("id") Long id) {
return service.getByIdAdmin(securityCtx, system, id);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<CategoryData>> getAllAdmin() {
return service.getAllAdmin(securityCtx, system);
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Uni<CategoryData> addOrUpdate(CategoryData data) {
return service.addOrUpdate(securityCtx, system, data);
}
@POST
@Path("sync")
@Consumes(MediaType.APPLICATION_JSON)
public Uni<?> syncCategory(CategoryFullData data) {
return service.syncCategory(securityCtx, system, data);
}
@DELETE
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> delete(@PathParam("id") Long id) {
return service.delete(securityCtx, system, id);
}
}

View File

@@ -1,354 +1,28 @@
package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.data.model.ClubModel;
import fr.titionfire.ffsaf.domain.service.ClubService;
import fr.titionfire.ffsaf.domain.service.PDFService;
import fr.titionfire.ffsaf.domain.service.TradService;
import fr.titionfire.ffsaf.domain.service.VirusScannerService;
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
import fr.titionfire.ffsaf.rest.data.*;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
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.*;
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.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.Comparator;
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
PDFService pdfService;
@Inject
SecurityCtx securityCtx;
@Inject
VirusScannerService scannerService;
@ConfigProperty(name = "upload_dir")
String media;
@Inject
TradService trad;
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
@RolesAllowed("federation_admin")
@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).sorted(
Comparator.comparing(SimpleClubModel::getName)).toList());
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(trad));
}
@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_tresorier", "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(trad)));
}
@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(() -> Utils.uploadFile(scannerService, input.getLogo(), id, media, "ppClub"))
.chain(() -> Utils.uploadFile(scannerService, input.getStatus(), id, media, "clubStatus"));
}
@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 -> Utils.uploadFile(scannerService, input.getLogo(), id, media, "ppClub"))
.call(id -> Utils.uploadFile(scannerService, input.getStatus(), id, media, "clubStatus"));
}
@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("/{id}/affiliation")
@RolesAllowed({"federation_admin"})
@Operation(summary = "Renvoie l'attestation d'affiliation du club en fonction de son identifiant", description =
"Renvoie l'attestation d'affiliation du club en fonction de son identifiant")
@APIResponses(value = {
@APIResponse(responseCode = "200", description = "L'attestation d'affiliation"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "404", description = "Le club n'existe pas ou n'a pas d'affiliation active"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Uni<Response> getAffiliation(@Parameter(description = "Identifiant de club") @PathParam("id") long id) {
return pdfService.getAffiliationPdf(id);
}
@GET
@Path("/me")
@RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "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(trad)));
}
@PUT
@Path("/me")
@RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "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("/me/affiliation")
@RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Operation(summary = "Renvoie l'attestation d'affiliation du club de l'utilisateur connecté", description =
"Renvoie l'attestation d'affiliation du club de l'utilisateur connecté")
@APIResponses(value = {
@APIResponse(responseCode = "200", description = "L'attestation d'affiliation"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "404", description = "Le club n'a pas d'affiliation active"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Uni<Response> getMeAffiliation() {
return pdfService.getAffiliationPdf(securityCtx.getSubject());
}
@GET
@Path("/members")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra", "club_tresorier"})
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Revoie tout les membres de votre club")
@APIResponses(value = {
@APIResponse(responseCode = "200", description = "List des membres"),
@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<List<VerySimpleMembre>> getMembers() {
return clubService.getMembers(securityCtx);
}
@GET
@Path("/renew/{id}")
@RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "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_tresorier", "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.getMediaFileNoDefault((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", "club_tresorier", "club_respo_intra"})
@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(), Uni.createFrom().nullItem(), false);
} catch (URISyntaxException e) {
throw new InternalError();
}
}));
}
@GET
@Path("get_map_data")
public Uni<List<ClubMapData>> getMapData() {
return clubService.getMapData();
}
}

View File

@@ -0,0 +1,136 @@
package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.MembreService;
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
import fr.titionfire.ffsaf.rest.from.FullMemberForm;
import fr.titionfire.ffsaf.utils.Pair;
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 java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLConnection;
import java.nio.file.Files;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
@Path("api/member")
public class CombEndpoints {
@Inject
MembreService membreService;
@ConfigProperty(name = "upload_dir")
String media;
@GET
@Path("/all")
@RolesAllowed("federation_admin")
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<SimpleMembre>> getAll() {
return membreService.getAll().map(membreModels -> membreModels.stream().map(SimpleMembre::fromModel).toList());
}
@GET
@Path("{id}")
@RolesAllowed("federation_admin")
@Produces(MediaType.APPLICATION_JSON)
public Uni<SimpleMembre> getById(@PathParam("id") long id) {
return membreService.getById(id).map(SimpleMembre::fromModel);
}
@POST
@Path("{id}")
@RolesAllowed("federation_admin")
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Uni<String> setAdminMembre(@PathParam("id") long id, FullMemberForm input) {
Future<String> future = CompletableFuture.supplyAsync(() -> {
try (InputStream is = new BufferedInputStream(new ByteArrayInputStream(input.getPhoto_data()))) {
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/" + input.getId() + extension).toPath(), input.getPhoto_data());
return "OK";
} catch (IOException e) {
return e.getMessage();
}
});
if (input.getPhoto_data().length > 0) {
return membreService.update(id, input)
.invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
}))
.chain(() -> Uni.createFrom().future(future))
.invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out);
}));
} else {
return membreService.update(id, input)
.invoke(Unchecked.consumer(out -> {
if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out);
}));
}
}
@GET
@Path("{id}/photo")
@RolesAllowed("federation_admin")
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 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();
});
}
}

View File

@@ -1,58 +0,0 @@
package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.CompetitionService;
import fr.titionfire.ffsaf.rest.data.CompetitionData;
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.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import java.util.List;
@Path("api/competition/admin")
public class CompetitionAdminEndpoints {
@Inject
CompetitionService service;
@Inject
SecurityCtx securityCtx;
@GET
@Path("{id}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<CompetitionData> getByIdAdmin(@PathParam("id") Long id) {
return service.getByIdAdmin(securityCtx, id);
}
@GET
@Path("all")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<CompetitionData>> getAllAdmin() {
return service.getAllAdmin(securityCtx);
}
@GET
@Path("all/{system}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<CompetitionData>> getAllSystemAdmin(@PathParam("system") CompetitionSystem system) {
return service.getAllSystemAdmin(securityCtx, system);
}
@GET
@Path("all/{system}/table")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<CompetitionData>> getAllSystemTable(@PathParam("system") CompetitionSystem system) {
return service.getAllSystemTable(securityCtx, system);
}
}

View File

@@ -1,135 +0,0 @@
package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.CompetitionService;
import fr.titionfire.ffsaf.rest.data.*;
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 org.eclipse.microprofile.openapi.annotations.Operation;
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, @QueryParam("light") boolean light) {
if (light)
return service.getById(securityCtx, id);
else
return service.getByIdAdmin(securityCtx, id);
}
@GET
@Path("{id}/register/{source}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<SimpleRegisterComb>> getRegister(@PathParam("id") Long id, @PathParam("source") String source) {
return service.getRegister(securityCtx, id, source);
}
@POST
@Path("{id}/register/{source}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
@Operation(hidden = true)
public Uni<SimpleRegisterComb> addRegisterComb(@PathParam("id") Long id, @PathParam("source") String source,
RegisterRequestData data) {
return service.addRegisterComb(securityCtx, id, data, source);
}
@POST
@Path("{id}/registers/{source}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
@Operation(hidden = true)
public Uni<List<SimpleRegisterComb>> addRegistersComb(@PathParam("id") Long id, @PathParam("source") String source,
List<RegisterRequestData> data) {
return service.addRegistersComb(securityCtx, id, data, source);
}
@DELETE
@Path("{id}/register/{comb_id}/{source}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
@Operation(hidden = true)
public Uni<Void> removeRegisterComb(@PathParam("id") Long id, @PathParam("comb_id") Long combId,
@PathParam("source") String source, @QueryParam("ban") boolean ban) {
return service.removeRegisterComb(securityCtx, id, combId, source, ban);
}
@GET
@Path("{id}/safcaData")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<SimpleCompetData> getSafcaData(@PathParam("id") Long id) {
return service.getSafcaData(securityCtx, id);
}
@GET
@Path("{id}/internalData")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<SimpleCompetData> getInternalData(@PathParam("id") Long id) {
return service.getInternalData(securityCtx, id);
}
@GET
@Path("{id}/categories")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<PresetData>> getPresetsForCompetition(@PathParam("id") Long id) {
return service.getPresetsForCompetition(securityCtx, id);
}
@GET
@Path("all")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<List<CompetitionData>> getAll() {
return service.getAll(securityCtx);
}
@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);
}
@POST
@Path("/internalData")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> setInternalData(SimpleCompetData data) {
return service.setInternalData(securityCtx, data);
}
@DELETE
@Path("{id}")
@Authenticated
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> delete(@PathParam("id") Long id) {
return service.delete(securityCtx, id);
}
}

View File

@@ -1,110 +1,48 @@
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.Pair;
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.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 {
@Inject
KeycloakService service;
@Inject
SecurityCtx securityCtx;
@Inject
Vertx vertx;
@GET
@Path("{id}")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "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 (!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);
@RolesAllowed("federation_admin")
public Uni<?> getCompte(@PathParam("id") String id) {
return service.fetchCompte(id);
}
@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);
}
@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);
}
@GET
@Path("{id}/roles")
@RolesAllowed("federation_admin")
@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) {
public Uni<?> 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<>();
@@ -115,8 +53,8 @@ public class CompteEndpoints {
else toRemove.add("safca_super_admin");
if (form.isSafca_user()) toAdd.add("safca_user");
else toRemove.add("safca_user");
if (form.isCreate_compet()) toAdd.add("create_compet");
else toRemove.add("create_compet");
if (form.isSafca_create_compet()) toAdd.add("safca_create_compet");
else toRemove.add("safca_create_compet");
return service.updateRole(id, toAdd, toRemove);
}

View File

@@ -1,34 +0,0 @@
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));
}
});
}
}

View File

@@ -1,84 +0,0 @@
package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.ResultService;
import fr.titionfire.ffsaf.domain.service.UpdateService;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import java.util.HashMap;
@Path("api/public/result/{id}")
public class ExternalResultEndpoints {
@Inject
ResultService resultService;
@Inject
UpdateService updateService;
@PathParam("id")
private String id;
@GET
@Path("/poule/list")
@Produces(MediaType.APPLICATION_JSON)
public Uni<HashMap<String, Long>> list() {
return resultService.getCategoryList(id);
}
@GET
@Path("/poule/data")
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> getArray(@QueryParam("poule") long poule, @DefaultValue("-1") @QueryParam("rf") long rf) {
if (poule == 0)
return Uni.createFrom().voidItem();
if (updateService.needUpdate(poule, rf)) {
return resultService.getCategory(id, poule);
} else {
return Uni.createFrom().voidItem();
}
}
@GET
@Path("/comb/list")
@Produces(MediaType.APPLICATION_JSON)
public Uni<HashMap<String, String>> combList() {
return resultService.getCombList(id);
}
@GET
@Path("/comb/data")
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> getArray(@QueryParam("comb") String comb) {
if (comb.equals("0"))
return Uni.createFrom().item("");
return resultService.getCombArrayPublic(id, comb);
}
@GET
@Path("/comb/get_all")
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> getAll() {
return resultService.getAllCombArrayPublic(id);
}
@GET
@Path("/club/list")
@Produces(MediaType.APPLICATION_JSON)
public Uni<HashMap<String, Long>> clubList() {
return resultService.getClubList(id);
}
@GET
@Path("/club/data")
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> getClubArray(@QueryParam("club") long club) {
if (club == 0)
return Uni.createFrom().item("");
return resultService.getClubArray2(id, club, null);
}
}

View File

@@ -1,204 +0,0 @@
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.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 java.util.List;
import java.util.function.Consumer;
@Path("api/licence")
public class LicenceEndpoints {
@Inject
LicenceService licenceService;
@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_tresorier", "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());
}
@GET
@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());
}
@GET
@Path("current/club")
@RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "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(securityCtx)
.map(licenceModels -> licenceModels.stream().map(SimpleLicence::fromModel).toList());
}
@POST
@Path("pay")
@RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Operation(summary = "Paiement des licence", description = "Retourne le lien de paiement pour les licence des membre fournie")
@APIResponses(value = {
@APIResponse(responseCode = "200", description = "Commande avec succès"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Uni<String> payLicences(@Parameter(description = "Id des membres") List<Long> ids) {
return licenceService.payLicences(ids, checkPerm, securityCtx);
}
@POST
@Path("{id}")
@RolesAllowed("federation_admin")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Operation(summary = "Créer une licence", description = "Créer une 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);
}
@POST
@Path("validate")
@RolesAllowed("federation_admin")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Operation(summary = "Validation licence", description = "Valide en masse les licences de l'année en cours (pour les administrateurs)")
@APIResponses(value = {
@APIResponse(responseCode = "200", description = "Les licences ont été mise à jour avec succès"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Uni<?> valideLicences(@Parameter(description = "Id des membres a valider") List<Long> ids) {
return licenceService.valideLicences(ids);
}
@POST
@Path("validate-pay")
@RolesAllowed("federation_admin")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Operation(summary = "Validation du payment des licences", description = "Valide en masse le payment des licences de l'année en cours (pour les administrateurs)")
@APIResponses(value = {
@APIResponse(responseCode = "200", description = "Les licences ont été mise à jour avec succès"),
@APIResponse(responseCode = "403", description = "Accès refusé"),
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
})
public Uni<?> validePaymentLicences(@Parameter(description = "Id des membres a valider") List<Long> ids) {
return licenceService.validePaymentLicences(ids);
}
@DELETE
@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);
}
@POST
@Path("club/{id}")
@RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "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);
}
@DELETE
@Path("club/{id}")
@RolesAllowed({"club_president", "club_secretaire", "club_tresorier", "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);
}
// TODO remove after importe all data
@GET
@Path("import")
@RolesAllowed({"federation_admin"})
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> setImport(@QueryParam("licence") int licence, @QueryParam("saison") int saison,
@QueryParam("valid") int valid) {
return licenceService.setImport(licence, saison, valid != 0);
}
}

Some files were not shown because too many files have changed in this diff Show More