Put and post methods work with error handling 🔨
continuous-integration/drone/push Build is failing Details

Quarkus_Continue
Emre KARTAL 2 years ago
parent 2d23793eb0
commit 4011ff18c6

@ -57,7 +57,7 @@ public class GameDto {
public Long hostID; public Long hostID;
@JsonProperty("winner") @JsonProperty("winner")
public UserTinyDTO winner; public UserTinyDto winner;
@JsonIgnore @JsonIgnore
@JsonProperty("rounds") @JsonProperty("rounds")
@ -72,7 +72,7 @@ public class GameDto {
Uni<List<ParticipeDto>> players, Uni<List<ParticipeDto>> players,
LocalDate time, LocalDate time,
Long ownerGame, Long ownerGame,
UserTinyDTO winner, UserTinyDto winner,
Uni<List<RoundDto>> rounds) { Uni<List<RoundDto>> rounds) {
this.players = players; this.players = players;
this.date = time; this.date = time;

@ -15,9 +15,9 @@ public class ParticipeDto {
public int totalPoints; public int totalPoints;
@Schema(description = "The User entity that this Participe belongs to") @Schema(description = "The User entity that this Participe belongs to")
public UserDTO user; public UserDto user;
public ParticipeDto(Long idUser, UserDTO user, Integer totalPoints, String guestName) { public ParticipeDto(Long idUser, UserDto user, Integer totalPoints, String guestName) {
this.idUser = idUser; this.idUser = idUser;
this.guestName = guestName; this.guestName = guestName;

@ -5,7 +5,7 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema;
@RegisterForReflection @RegisterForReflection
@Schema(description = "A DTO for transferring user details") @Schema(description = "A DTO for transferring user details")
public class UserDTO { public class UserDto {
public Long id; public Long id;
public String name; public String name;
@ -13,7 +13,7 @@ public class UserDTO {
public String mail; public String mail;
//public UserStatsDTO stats; //public UserStatsDTO stats;
public UserDTO(Long id, String name, String image, String mail) { public UserDto(Long id, String name, String image, String mail) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.image = image; this.image = image;

@ -5,7 +5,7 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema;
@RegisterForReflection @RegisterForReflection
@Schema(description = "A DTO describing the stats of a User") @Schema(description = "A DTO describing the stats of a User")
public class UserStatsDTO { public class UserStatsDto {
public Long nbVictories; public Long nbVictories;
public Long nbGames; public Long nbGames;
public Long highscore; public Long highscore;
@ -14,8 +14,8 @@ public class UserStatsDTO {
public Double avgScore; public Double avgScore;
public Double avgPinsPerRound; public Double avgPinsPerRound;
public UserStatsDTO(Long nbVictories, Long nbGames, Long highscore, Long nbStrikes, Long nbSpares, Double avgScore, public UserStatsDto(Long nbVictories, Long nbGames, Long highscore, Long nbStrikes, Long nbSpares, Double avgScore,
Double avgPinsPerRound) { Double avgPinsPerRound) {
this.nbVictories = nbVictories; this.nbVictories = nbVictories;
this.nbGames = nbGames; this.nbGames = nbGames;
this.highscore = highscore; this.highscore = highscore;

@ -1,16 +1,20 @@
package org.acme.api.dto; package org.acme.api.dto;
import io.quarkus.hibernate.reactive.panache.common.ProjectedFieldName;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;
import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.eclipse.microprofile.openapi.annotations.media.Schema;
@RegisterForReflection @RegisterForReflection
@Schema(description = "A minimal DTO for transferring user information") @Schema(description = "A minimal DTO for transferring user information")
public class UserTinyDTO { public class UserTinyDto {
public Long id;
public String name; public String name;
public UserTinyDTO(Long id, String name) { public String image;
this.id = id; public String mail;
public String password;
public UserTinyDto(String name, String image, String mail, String password) {
this.name = name; this.name = name;
this.image = image;
this.mail = mail;
this.password = password;
} }
} }

@ -6,7 +6,7 @@ import java.util.stream.Collectors;
import javax.ws.rs.NotFoundException; import javax.ws.rs.NotFoundException;
import org.acme.api.dto.GameDto; import org.acme.api.dto.GameDto;
import org.acme.api.mappeur.GameMappeur; import org.acme.api.mapper.GameMapper;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
@ -19,20 +19,20 @@ public class GameManager {
} }
public Uni<GameDto> saveGame(GameDto game) { public Uni<GameDto> saveGame(GameDto game) {
return dbManager.dbContext.gameRepository.persist(GameMappeur.toEntity(game, dbManager.dbContext)) return dbManager.dbContext.gameRepository.persist(GameMapper.toEntity(game, dbManager.dbContext))
.onItem().transform(gameEntity -> GameMappeur.toDto(gameEntity, dbManager.dbContext)); .onItem().transform(gameEntity -> GameMapper.toDto(gameEntity, dbManager.dbContext));
} }
public Uni<GameDto> getDetailsGameById(Long gameId) { public Uni<GameDto> getDetailsGameById(Long gameId) {
return dbManager.dbContext.gameRepository.findById(gameId) return dbManager.dbContext.gameRepository.findById(gameId)
.onItem().ifNull().failWith(new NotFoundException("Game not found")) .onItem().ifNull().failWith(new NotFoundException("Game not found"))
.onItem().transform(gameEntity -> GameMappeur.toDto(gameEntity, dbManager.dbContext)); .onItem().transform(gameEntity -> GameMapper.toDto(gameEntity, dbManager.dbContext));
} }
public Uni<List<GameDto>> getAllGames() { public Uni<List<GameDto>> getAllGames() {
return dbManager.dbContext.gameRepository.findAll().list() return dbManager.dbContext.gameRepository.findAll().list()
.onItem().transform(games -> games.stream() .onItem().transform(games -> games.stream()
.map(gameEntity -> GameMappeur.toDto(gameEntity, dbManager.dbContext)) .map(gameEntity -> GameMapper.toDto(gameEntity, dbManager.dbContext))
.collect(Collectors.toList())) .collect(Collectors.toList()))
.onFailure().invoke(throwable -> { .onFailure().invoke(throwable -> {
// Log the error or perform any other error handling here // Log the error or perform any other error handling here

@ -1,5 +0,0 @@
package org.acme.api.mappeur;
public class TrhowMappeur {
}

@ -1,18 +0,0 @@
package org.acme.api.mappeur;
import org.acme.api.dto.UserDTO;
import org.acme.api.dto.UserTinyDTO;
import org.acme.hibernates.entities.UserEntity;
public class UserMappeur {
public static UserDTO toUserDto(UserEntity entity) {
/*return new UserDTO(entity.id, entity.name, entity.stats.getNbVictories(), entity.stats.getNbGames(),
entity.stats.getHighscore(), entity.stats.getNbStrikes(), entity.stats.getNbSpares(),
entity.stats.getAvgScore(), entity.stats.getAvgPinsPerRound());*/
return new UserDTO((long)2, "f","fd","fd");
}
public static UserTinyDTO toUserTinyDTO(UserEntity entity) {
return new UserTinyDTO(entity.id, entity.getName());
}
}

@ -1,26 +1,19 @@
package org.acme.api.controllers; package org.acme.api.controllers;
import java.net.URI;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.ws.rs.Consumes; import javax.ws.rs.*;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import org.acme.api.BowlDbContext; import org.acme.api.BowlDbContext;
import org.acme.api.dto.UserDTO; import org.acme.api.dto.UserDto;
import org.acme.hibernates.entities.UserEntity; import org.acme.api.dto.UserTinyDto;
import org.acme.api.mapper.UserMapper;
import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -42,9 +35,10 @@ public class UserController {
@GET @GET
@Operation(summary = "Get all users") @Operation(summary = "Get all users")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Uni<List<UserDTO>> getUsers() { public Uni<List<UserDto>> getUsers() {
LOGGER.info("Get all users"); LOGGER.info("Get all users");
return service.userRepository.findAll().project(UserDTO.class).list(); return service.userRepository.findAll().list()
.map(entities -> entities.stream().map(UserMapper::toDto).collect(Collectors.toList()));
} }
@GET @GET
@ -66,9 +60,17 @@ public class UserController {
@Path("/name/{name}") @Path("/name/{name}")
public Uni<Response> getUserByName(@PathParam("name") String name) { public Uni<Response> getUserByName(@PathParam("name") String name) {
LOGGER.info("Get user with name : " + name); LOGGER.info("Get user with name : " + name);
return service.userRepository.findwithName(name)
.map(user -> user == null ? Response.status(Status.NOT_FOUND) : Response.ok(user).status(200)) return service.userRepository.verifiedName(name)
.map(Response.ResponseBuilder::build); .flatMap(exist -> {
if (exist) {
return service.userRepository.findWithName(name)
.map(user -> Response.ok(user).build());
} else {
throw new NotFoundException("User not found");
}
});
} }
@GET @GET
@ -78,9 +80,16 @@ public class UserController {
@Path("/mail/{mail}") @Path("/mail/{mail}")
public Uni<Response> getUserByMail(@PathParam("mail") String mail) { public Uni<Response> getUserByMail(@PathParam("mail") String mail) {
LOGGER.info("Get user with mail : " + mail); LOGGER.info("Get user with mail : " + mail);
return service.userRepository.findwithMail(mail)
.map(user -> user == null ? Response.status(Status.NOT_FOUND) : Response.ok(user).status(200)) return service.userRepository.verifiedMail(mail)
.map(Response.ResponseBuilder::build); .flatMap(exist -> {
if (exist) {
return service.userRepository.findWithMail(mail)
.map(user -> Response.ok(user).build());
} else {
throw new NotFoundException("User not found");
}
});
} }
@POST @POST
@ -88,35 +97,67 @@ public class UserController {
@APIResponse(responseCode = "201", description = "User successfully created") @APIResponse(responseCode = "201", description = "User successfully created")
@APIResponse(responseCode = "422", description = "User invalidly set on request") @APIResponse(responseCode = "422", description = "User invalidly set on request")
@ReactiveTransactional @ReactiveTransactional
public Uni<Response> createUser(UserEntity user) { public Uni<Response> createUser(UserTinyDto user) {
if (user == null) { if (user == null) {
throw new WebApplicationException("user was invalidly set on request.", 422); throw new WebApplicationException("user was invalidly set on request.", 422);
} }
LOGGER.info("creating user: " + user.getName()); LOGGER.info("creating user: " + user);
return service.userRepository.persist(user) return Uni.combine().all().unis(
.map(persistedUser -> Response service.userRepository.verifiedName(user.name),
.created(URI.create("/users/" + user.id)) service.userRepository.verifiedMail(user.mail))
.entity(persistedUser) .asTuple()
.build()) .flatMap(tuple -> {
boolean nameExists = tuple.getItem1();
boolean mailExists = tuple.getItem2();
if (nameExists) {
throw new WebApplicationException("Name already exists", Status.BAD_REQUEST);
}
if (mailExists) {
throw new WebApplicationException("Mail already exists", Status.BAD_REQUEST);
}
return service.userRepository.addUser(user)
.map(createdUser -> Response.status(Response.Status.CREATED).entity(createdUser).build());
})
.onFailure().recoverWithItem(Response.status(Status.BAD_REQUEST).build()); .onFailure().recoverWithItem(Response.status(Status.BAD_REQUEST).build());
} }
@PUT @PUT
@Operation(summary = "Update a user") @Operation(summary = "Update a user")
@APIResponse(responseCode = "200", description = "OK") @APIResponse(responseCode = "200", description = "OK")
@APIResponse(responseCode = "400", description = "Bad Request")
@APIResponse(responseCode = "404", description = "User not found") @APIResponse(responseCode = "404", description = "User not found")
@Path("/{id}") @Path("/{id}")
@ReactiveTransactional @ReactiveTransactional
public Uni<Response> updateUser(@PathParam("id") Long id, UserEntity newUser) { public Uni<Response> updateUser(@PathParam("id") Long id, UserTinyDto newUser) {
LOGGER.info("Update user with id : " + id); LOGGER.info("Update user with id : " + id);
return service.userRepository.findById(id) return service.userRepository.findById(id)
.onItem().ifNull().failWith(() -> new WebApplicationException("User not found", Status.NOT_FOUND)) .onItem().ifNull().failWith(() -> new WebApplicationException("User not found", Status.NOT_FOUND))
.onItem().ifNotNull().invoke(oldUser -> { .onItem().ifNotNull().transformToUni(oldUser ->
oldUser.setName(newUser.getName()); Uni.combine().all().unis(
}) service.userRepository.verifiedName(newUser.name),
.onItem().ifNotNull().transform(entity -> Response.ok(entity).build()); service.userRepository.verifiedMail(newUser.mail))
.asTuple()
.flatMap(tuple -> {
boolean nameExists = tuple.getItem1();
boolean mailExists = tuple.getItem2();
if (nameExists && !oldUser.getName().equalsIgnoreCase(newUser.name)) {
throw new WebApplicationException("Name already exists", Status.BAD_REQUEST);
}
if (mailExists && !oldUser.getMail().equalsIgnoreCase(newUser.mail)) {
throw new WebApplicationException("Mail already exists", Status.BAD_REQUEST);
}
oldUser.setName(newUser.name);
oldUser.setMail(newUser.mail);
oldUser.setImage(newUser.image);
return service.userRepository.persistAndFlush(oldUser)
.map(entity -> Response.ok(UserMapper.toDto(entity)).build());
})
);
} }
@DELETE @DELETE
@Operation(summary = "Delete a user") @Operation(summary = "Delete a user")
@APIResponse(responseCode = "204") @APIResponse(responseCode = "204")

@ -1,4 +1,4 @@
package org.acme.api.mappeur; package org.acme.api.mapper;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId; import java.time.ZoneId;
@ -17,7 +17,7 @@ import io.smallrye.mutiny.Uni;
public class Extensions { public class Extensions {
public static Uni<List<RoundDto>> toRoundDtoList(Uni<List<RoundEntity>> uni) { public static Uni<List<RoundDto>> toRoundDtoList(Uni<List<RoundEntity>> uni) {
return uni.map(roundEntities -> roundEntities.stream() return uni.map(roundEntities -> roundEntities.stream()
.map(RoundMappeur::toDto) .map(RoundMapper::toDto)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
@ -26,7 +26,7 @@ public class Extensions {
.onItem().transformToUni(participes -> { .onItem().transformToUni(participes -> {
List<ParticipeDto> dtos = new ArrayList<>(); List<ParticipeDto> dtos = new ArrayList<>();
for (ParticipeEntity entity : participes) { for (ParticipeEntity entity : participes) {
dtos.add(ParticipeMappeur.toDto(entity)); dtos.add(ParticipeMapper.toDto(entity));
} }
return Uni.createFrom().item(dtos); return Uni.createFrom().item(dtos);
}) })

@ -1,10 +1,10 @@
package org.acme.api.mappeur; package org.acme.api.mapper;
import org.acme.api.BowlDbContext; import org.acme.api.BowlDbContext;
import org.acme.api.dto.GameDto; import org.acme.api.dto.GameDto;
import org.acme.hibernates.entities.GameEntity; import org.acme.hibernates.entities.GameEntity;
public class GameMappeur { public class GameMapper {
public static GameDto toDto(GameEntity entity, BowlDbContext dbContext) { public static GameDto toDto(GameEntity entity, BowlDbContext dbContext) {
GameDto dto = new GameDto(); GameDto dto = new GameDto();

@ -1,11 +1,11 @@
package org.acme.api.mappeur; package org.acme.api.mapper;
import org.acme.api.dto.ParticipeDto; import org.acme.api.dto.ParticipeDto;
import org.acme.hibernates.entities.ParticipeEntity; import org.acme.hibernates.entities.ParticipeEntity;
public class ParticipeMappeur { public class ParticipeMapper {
public static ParticipeDto toDto(ParticipeEntity entity) { public static ParticipeDto toDto(ParticipeEntity entity) {
return new ParticipeDto(entity.user.id, UserMappeur.toUserDto(entity.user), entity.totalPoints, return new ParticipeDto(entity.user.id, UserMapper.toDto(entity.user), entity.totalPoints,
entity.guestName); entity.guestName);
} }

@ -1,4 +1,4 @@
package org.acme.api.mappeur; package org.acme.api.mapper;
import java.util.List; import java.util.List;
@ -6,7 +6,7 @@ import org.acme.api.dto.RoundDto;
import org.acme.hibernates.entities.RoundEntity; import org.acme.hibernates.entities.RoundEntity;
import org.acme.hibernates.entities.ThrowEntity; import org.acme.hibernates.entities.ThrowEntity;
public class RoundMappeur { public class RoundMapper {
public static RoundDto toDto(RoundEntity entity) { public static RoundDto toDto(RoundEntity entity) {
List<ThrowEntity> throwsGame = entity.throwsGame; List<ThrowEntity> throwsGame = entity.throwsGame;

@ -0,0 +1,5 @@
package org.acme.api.mapper;
public class TrhowMapper {
}

@ -0,0 +1,38 @@
package org.acme.api.mapper;
import org.acme.api.dto.UserDto;
import org.acme.api.dto.UserTinyDto;
import org.acme.hibernates.entities.UserEntity;
import java.security.MessageDigest;
public class UserMapper {
private static final String HASH_ALGORITHM = "SHA-256";
public static UserDto toDto(UserEntity user) {
return new UserDto(user.id, user.getName(), user.getImage(), user.getMail());
}
public static UserEntity toEntity(UserTinyDto user) {
try {
MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
byte[] hashedBytes = digest.digest(user.password.getBytes("UTF-8"));
String hashedPassword = bytesToHex(hashedBytes);
return new UserEntity(user.name.toLowerCase(), user.image, user.mail.toLowerCase(), hashedPassword);
} catch (Exception ex) {
throw new RuntimeException("Error hashing password", ex);
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xFF & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
}

@ -2,29 +2,53 @@ package org.acme.api.service;
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ApplicationScoped;
import org.acme.api.dto.UserDTO; import org.acme.api.dto.UserDto;
import org.acme.api.dto.UserTinyDto;
import org.acme.api.mapper.UserMapper;
import org.acme.hibernates.entities.UserEntity; import org.acme.hibernates.entities.UserEntity;
import io.quarkus.hibernate.reactive.panache.PanacheRepository; import io.quarkus.hibernate.reactive.panache.PanacheRepository;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import java.util.Objects;
@ApplicationScoped @ApplicationScoped
public class UserRepository implements PanacheRepository<UserEntity> { public class UserRepository implements PanacheRepository<UserEntity> {
// public Uni<UserEntity> getUserByName(String name) { public Uni<UserDto> addUser(UserTinyDto user) {
// return UserEntity.find("name", name).firstResult(); UserEntity userEntity = UserMapper.toEntity(user);
// } return persistAndFlush(userEntity)
.map(ignore -> UserMapper.toDto(userEntity));
}
public Uni<UserDto> findWithName(String name) {
return find("name", name.toLowerCase())
.firstResult()
.map(UserMapper::toDto);
}
public Uni<Boolean> verifiedName(String name) {
return find("name", name.toLowerCase())
.firstResult()
.map(Objects::nonNull);
}
public Uni<UserDTO> findwithName(String name) { public Uni<Boolean> verifiedMail(String mail) {
return find("name", name.toLowerCase()).project(UserDTO.class).firstResult(); return find("mail", mail.toLowerCase())
.firstResult()
.map(Objects::nonNull);
} }
public Uni<UserDTO> findwithMail(String mail) { public Uni<UserDto> findWithMail(String mail) {
return find("mail", mail.toLowerCase()).project(UserDTO.class).firstResult(); return find("mail", mail.toLowerCase())
.firstResult()
.map(UserMapper::toDto);
} }
public Uni<UserDTO> getUserById(Long id) { public Uni<UserDto> getUserById(Long id) {
return find("id", id).project(UserDTO.class).firstResult(); return find("id", id)
.firstResult()
.map(UserMapper::toDto);
} }
} }

@ -29,9 +29,17 @@ public class UserEntity {
//this.stats = new UserStatsEntity(this); //this.stats = new UserStatsEntity(this);
} }
public UserEntity(String name, String image, String mail, String password) {
this.name = name;
this.image = image;
this.mail = mail;
this.password = password;
//this.stats = new UserStatsEntity(this);
}
// return name as uppercase in the model // return name as uppercase in the model
public String getName() { public String getName() {
return name.toUpperCase(); return name.substring(0, 1).toUpperCase() + name.substring(1);
} }
// store all names in lowercase in the DB // store all names in lowercase in the DB
@ -44,7 +52,23 @@ public class UserEntity {
} }
public void setPassword(String password) { public void setPassword(String password) {
this.password = password.toLowerCase(); this.password = password;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail.toLowerCase();
} }
} }

@ -1,9 +1,9 @@
INSERT INTO INSERT INTO
Users (name, password, image, mail) Users (name, password, image, mail)
VALUES VALUES
('david', 'password123','','david.d_almeida@etu.uca.fr'), ('david', '38762cf7f55934b34d179ae6a4c80cadccbb7f0a2c2a5b4c4a1cb595d8dadb31','','david.d_almeida@etu.uca.fr'),
('emre', 'password2','','emre.kartal@etu.uca.fr'), ('emre', 'af7c70f8789d9e0fde5b5f18b61c5e5dc5d5c5f5e7e5c4f4be7b2cb5d57ef52c','','emre.kartal@etu.uca.fr'),
('arthur', 'password3','','arthur.valin@etu.uca.fr'); ('arthur', '0cc175b9c0f1b6a831c399e269772661','','arthur.valin@etu.uca.fr');
/*INSERT INTO games (isFinished, nbPoints, time, winner, host_id) VALUES (false, 0, CURRENT_TIMESTAMP, 0, 1); /*INSERT INTO games (isFinished, nbPoints, time, winner, host_id) VALUES (false, 0, CURRENT_TIMESTAMP, 0, 1);
INSERT INTO participe (idGame, position, guestname, totalpoints, iduser) VALUES (1, 1, 'Alice', 0, 1); INSERT INTO participe (idGame, position, guestname, totalpoints, iduser) VALUES (1, 1, 'Alice', 0, 1);

Loading…
Cancel
Save