feat: club page

wip: edit af request
This commit is contained in:
2024-07-17 23:11:59 +02:00
parent 43f7a54b15
commit b93a08da71
16 changed files with 572 additions and 257 deletions

View File

@@ -53,11 +53,10 @@ public class AffiliationService {
return repositoryRequest.listAll();
}
public Uni<String> save(AffiliationRequestForm form) {
public Uni<AffiliationRequestModel> pre_save(AffiliationRequestForm form, boolean unique) {
AffiliationRequestModel affModel = form.toModel();
int currentSaison = Utils.getSaison();
// noinspection ResultOfMethodCallIgnored
return Uni.createFrom().item(affModel)
.invoke(Unchecked.consumer(model -> {
if (model.getSaison() != currentSaison && model.getSaison() != currentSaison + 1) {
@@ -67,7 +66,7 @@ public class AffiliationService {
.chain(() -> repositoryRequest.count("siret = ?1 and saison = ?2", affModel.getSiret(),
affModel.getSaison()))
.onItem().invoke(Unchecked.consumer(count -> {
if (count != 0) {
if (count != 0 && unique) {
throw new IllegalArgumentException("Affiliation request already exists");
}
}))
@@ -80,26 +79,60 @@ public class AffiliationService {
}))
.map(o -> affModel)
.call(model -> ((model.getM1_lincence() != -1) ? combRepository.find("licence",
model.getM1_lincence()).count().invoke(count -> {
model.getM1_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) {
throw new IllegalArgumentException("Licence membre n°1 inconnue");
}
}) : Uni.createFrom().nullItem())
})) : Uni.createFrom().nullItem())
)
.call(model -> ((model.getM2_lincence() != -1) ? combRepository.find("licence",
model.getM2_lincence()).count().invoke(count -> {
model.getM2_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) {
throw new IllegalArgumentException("Licence membre n°2 inconnue");
}
}) : Uni.createFrom().nullItem())
})) : Uni.createFrom().nullItem())
)
.call(model -> ((model.getM3_lincence() != -1) ? combRepository.find("licence",
model.getM3_lincence()).count().invoke(count -> {
model.getM3_lincence()).count().invoke(Unchecked.consumer(count -> {
if (count == 0) {
throw new IllegalArgumentException("Licence membre n°3 inconnue");
}
}) : Uni.createFrom().nullItem())
).chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model)))
})) : Uni.createFrom().nullItem())
);
}
public Uni<?> saveEdit(AffiliationRequestForm form) {
return pre_save(form, false)
.chain(model -> repositoryRequest.findById(form.getId())
.onItem().ifNull().failWith(new NotFoundException("Affiliation request not found"))
.chain(origine -> {
origine.setName(model.getName());
origine.setRNA(model.getRNA());
origine.setAddress(model.getAddress());
origine.setM1_lname(model.getM1_lname());
origine.setM1_fname(model.getM1_fname());
origine.setM1_lincence(model.getM1_lincence());
origine.setM1_role(model.getM1_role());
origine.setM1_email(model.getM1_email());
origine.setM2_lname(model.getM2_lname());
origine.setM2_fname(model.getM2_fname());
origine.setM2_lincence(model.getM2_lincence());
origine.setM2_role(model.getM2_role());
origine.setM2_email(model.getM2_email());
origine.setM3_lname(model.getM3_lname());
origine.setM3_fname(model.getM3_fname());
origine.setM3_lincence(model.getM3_lincence());
origine.setM3_role(model.getM3_role());
origine.setM3_email(model.getM3_email());
return Panache.withTransaction(() -> repositoryRequest.persist(origine));
}));
}
public Uni<String> save(AffiliationRequestForm form) {
// noinspection ResultOfMethodCallIgnored
return pre_save(form, true)
.chain(model -> Panache.withTransaction(() -> repositoryRequest.persist(model)))
.onItem()
.invoke(model -> Uni.createFrom().future(Utils.replacePhoto(model.getId(), form.getLogo(), media,
"aff_request/logo")))

View File

@@ -2,18 +2,19 @@ package fr.titionfire.ffsaf.domain.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import fr.titionfire.ffsaf.data.model.AffiliationModel;
import fr.titionfire.ffsaf.data.model.ClubModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.data.repository.ClubRepository;
import fr.titionfire.ffsaf.data.repository.CombRepository;
import fr.titionfire.ffsaf.net2.ServerCustom;
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
import fr.titionfire.ffsaf.net2.request.SReqClub;
import fr.titionfire.ffsaf.rest.data.RenewAffData;
import fr.titionfire.ffsaf.rest.data.SimpleClubList;
import fr.titionfire.ffsaf.rest.from.FullClubForm;
import fr.titionfire.ffsaf.utils.Contact;
import fr.titionfire.ffsaf.utils.PageResult;
import fr.titionfire.ffsaf.utils.RoleAsso;
import fr.titionfire.ffsaf.utils.Utils;
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;
@@ -25,10 +26,14 @@ import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.NotFoundException;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.hibernate.reactive.mutiny.Mutiny;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -114,6 +119,44 @@ public class ClubService {
return repository.find("clubId", clubId).firstResult();
}
public Uni<ClubModel> getOfUser(JsonWebToken idToken) {
return combRepository.find("userId = ?1", idToken.getSubject()).firstResult().invoke(Unchecked.consumer(m -> {
if (m == null || m.getClub() == null)
throw new NotFoundException("Club not found");
}))
.map(MembreModel::getClub)
.call(club -> Mutiny.fetch(club.getContact()));
}
public Uni<String> updateOfUser(JsonWebToken idToken, PartClubForm form) {
TypeReference<HashMap<Contact, String>> typeRef = new TypeReference<>() {
};
return combRepository.find("userId = ?1", idToken.getSubject()).firstResult().invoke(Unchecked.consumer(m -> {
if (m == null || m.getClub() == null)
throw new NotFoundException("Club not found");
if (!GroupeUtils.isInClubGroup(m.getClub().getId(), idToken))
throw new ForbiddenException();
}))
.map(MembreModel::getClub)
.call(club -> Mutiny.fetch(club.getContact()))
.chain(Unchecked.function(club -> {
club.setContact_intern(form.getContact_intern());
club.setAddress(form.getAddress());
try {
club.setContact(MAPPER.readValue(form.getContact(), typeRef));
} catch (JsonProcessingException e) {
throw new BadRequestException();
}
club.setTraining_location(form.getTraining_location());
club.setTraining_day_time(form.getTraining_day_time());
return Panache.withTransaction(() -> repository.persist(club));
}))
.map(__ -> "OK");
}
public Uni<String> update(long id, FullClubForm input) {
return repository.findById(id).call(m -> Mutiny.fetch(m.getContact()))
.onItem().transformToUni(Unchecked.function(m -> {
@@ -201,4 +244,29 @@ public class ClubService {
.call(__ -> Utils.deleteMedia(id, media, "ppClub"))
.call(__ -> Utils.deleteMedia(id, media, "clubStatus"));
}
public Uni<RenewAffData> getRenewData(long id) {
RenewAffData data = new RenewAffData();
return repository.findById(id)
.call(clubModel -> Mutiny.fetch(clubModel.getAffiliations()))
.invoke(clubModel -> {
data.setName(clubModel.getName());
data.setSiret(clubModel.getSIRET());
data.setRna(clubModel.getRNA());
data.setAddress(clubModel.getAddress());
data.setSaison(
clubModel.getAffiliations().stream().max(Comparator.comparing(AffiliationModel::getSaison))
.map(AffiliationModel::getSaison).map(i -> Math.min(i + 1, Utils.getSaison() + 1))
.orElse(Utils.getSaison()));
})
.chain(club -> combRepository.list("club = ?1", club))
.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)
.limit(3)
.map(RenewAffData.RenewMember::new)
.toList()))
.map(o -> data);
}
}

View File

@@ -63,10 +63,13 @@ public class AffiliationEndpoints {
@GET
@Path("/request/{id}")
@RolesAllowed({"federation_admin"})
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
public Uni<SimpleReqAffiliation> getAffRequest(@PathParam("id") long id) {
return service.getRequest(id);
return service.getRequest(id).invoke(Unchecked.consumer(o -> {
if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin"))
throw new ForbiddenException();
})).invoke(o -> checkPerm.accept(o.getClub()));
}
@DELETE
@@ -74,7 +77,11 @@ public class AffiliationEndpoints {
@RolesAllowed({"federation_admin"})
@Produces(MediaType.APPLICATION_JSON)
public Uni<?> getDelAffRequest(@PathParam("id") long id) {
return service.deleteReqAffiliation(id);
return service.getRequest(id).invoke(Unchecked.consumer(o -> {
if (o.getClub() == null && !securityIdentity.getRoles().contains("federation_admin"))
throw new ForbiddenException();
})).invoke(o -> checkPerm.accept(o.getClub()))
.chain(o -> service.deleteReqAffiliation(id));
}
@PUT
@@ -86,6 +93,15 @@ public class AffiliationEndpoints {
return service.saveAdmin(form);
}
@PUT
@Path("/request/edit")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Uni<?> saveEditAffRequest(AffiliationRequestForm form) {
return service.saveEdit(form);
}
@PUT
@Path("/request/apply")
@RolesAllowed({"federation_admin"})

View File

@@ -3,9 +3,11 @@ package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.data.model.ClubModel;
import fr.titionfire.ffsaf.domain.service.ClubService;
import fr.titionfire.ffsaf.net2.data.SimpleClubModel;
import fr.titionfire.ffsaf.rest.data.RenewAffData;
import fr.titionfire.ffsaf.rest.data.SimpleClub;
import fr.titionfire.ffsaf.rest.data.SimpleClubList;
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.GroupeUtils;
import fr.titionfire.ffsaf.utils.PageResult;
@@ -44,11 +46,15 @@ public class ClubEndpoints {
@ConfigProperty(name = "upload_dir")
String media;
Consumer<ClubModel> checkPerm = Unchecked.consumer(membreModel -> {
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(membreModel.getId(),
Consumer<ClubModel> checkPerm = Unchecked.consumer(clubModel -> {
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(clubModel.getId(),
idToken))
throw new ForbiddenException();
});
Consumer<Long> checkPerm2 = Unchecked.consumer(id -> {
if (!securityIdentity.getRoles().contains("federation_admin") && !GroupeUtils.isInClubGroup(id, idToken))
throw new ForbiddenException();
});
@GET
@Path("/no_detail")
@@ -125,7 +131,6 @@ public class ClubEndpoints {
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Uni<Long> addAdminClub(FullClubForm input) {
System.out.println(input);
return clubService.add(input)
.invoke(Unchecked.consumer(id -> {
if (id == null) throw new InternalError("Fail to create club data");
@@ -152,6 +157,31 @@ public class ClubEndpoints {
return clubService.delete(id);
}
@GET
@Path("/me")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
public Uni<SimpleClub> getOfUser() {
return clubService.getOfUser(idToken).map(SimpleClub::fromModel)
.invoke(m -> m.setContactMap(Contact.toSite()));
}
@PUT
@Path("/me")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Uni<String> setClubOfUser(PartClubForm form) {
return clubService.updateOfUser(idToken, form);
}
@GET
@Path("/renew/{id}")
@RolesAllowed({"club_president", "club_secretaire", "club_respo_intra"})
@Produces(MediaType.APPLICATION_JSON)
public Uni<RenewAffData> getOfUser(@PathParam("id") long id) {
return Uni.createFrom().item(id).invoke(checkPerm2).chain(__ -> clubService.getRenewData(id));
}
@GET
@Path("{clubId}/logo")

View File

@@ -198,32 +198,6 @@ public class CombEndpoints {
return membreService.delete(id, idToken);
}
private Future<String> replacePhoto(long id, byte[] input) {
return CompletableFuture.supplyAsync(() -> {
try (InputStream is = new BufferedInputStream(new ByteArrayInputStream(input))) {
String mimeType = URLConnection.guessContentTypeFromStream(is);
String[] detectedExtensions = MimeTypes.findExtensionsByMimeTypes(mimeType, false);
if (detectedExtensions.length == 0)
throw new IOException("Fail to detect file extension for MIME type " + mimeType);
FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id));
File[] files = new File(media, "ppMembre").listFiles(filter);
if (files != null) {
for (File file : files) {
//noinspection ResultOfMethodCallIgnored
file.delete();
}
}
String extension = "." + detectedExtensions[0];
Files.write(new File(media, "ppMembre/" + id + extension).toPath(), input);
return "OK";
} catch (IOException e) {
return e.getMessage();
}
});
}
@GET
@Path("{id}/photo")
@RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_respo_intra"})

View File

@@ -0,0 +1,45 @@
package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.utils.RoleAsso;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@RegisterForReflection
public class RenewAffData {
String name;
Long siret;
String rna;
String address;
int saison;
List<RenewMember> members;
@Data
@AllArgsConstructor
@RegisterForReflection
public static class RenewMember {
String lname;
String fname;
String email;
int licence;
RoleAsso role;
public RenewMember(MembreModel o) {
this.lname = o.getLname();
this.fname = o.getFname();
this.email = o.getEmail();
this.licence = o.getLicence();
this.role = o.getRole();
}
}
}

View File

@@ -1,4 +0,0 @@
package fr.titionfire.ffsaf.rest.from;
public class AffiliationForm {
}

View File

@@ -11,6 +11,9 @@ import org.jboss.resteasy.reactive.PartType;
@Getter
@ToString
public class AffiliationRequestForm {
@FormParam("id")
private Long id = null;
@FormParam("name")
private String name = null;
@FormParam("siret")

View File

@@ -0,0 +1,27 @@
package fr.titionfire.ffsaf.rest.from;
import jakarta.ws.rs.FormParam;
import lombok.Getter;
import lombok.ToString;
@ToString
@Getter
public class PartClubForm {
@FormParam("id")
private String id = null;
@FormParam("contact")
private String contact = null;
@FormParam("training_location")
private String training_location = null;
@FormParam("training_day_time")
private String training_day_time = null;
@FormParam("contact_intern")
private String contact_intern = null;
@FormParam("address")
private String address = null;
}