diff --git a/README.md b/README.md index 82912ab..4c4932a 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,9 @@ - [Schema Versioning Pattern](#schema-versioning-pattern) - [Incremental Document Migration](#incremental-document-migration) - [🐕‍🦺Services](#services) + - [🌺Special requests](#special-requests) + - [`Pokemong` by nickname](#pokemong-by-nickname) + - [`Pokemong` in date interval](#pokemong-in-date-interval) - [Prep steps](#prep-steps) - [♨️Java version](#java-version) - [🔐Database connection](#database-connection) @@ -272,7 +275,7 @@ classDiagram +deleteOneById(String id) +updateOne(Trainer trainer): Trainer -transferNewlyArrivedTrainerPokemongs(...) - } + } class PokemongService { -PokemongRepository pokemongRepository @@ -288,13 +291,27 @@ classDiagram +findByMove(String id): List~Pokemong~ +isEvoValid(String id, PokemongName species): boolean +batchUpdatePokemongTrainers(...) - } + } GenericService <|-- "T <- Move" MoveService GenericService <|-- "T <- Trainer" TrainerService GenericService <|-- "T <- Pokemong" PokemongService ``` +### 🌺Special requests + +This API goes a little bit beyond basic CRUD operations. + +#### `Pokemong` by nickname + +Using a MongoDB filter with a regex, `pokemongs` are searchable by nickname with the URL `/pokemong/nickname/{nickname}` +where `{nickname}` is a partial, case-insensitive search term. + +#### `Pokemong` in date interval + +Users can also use the route `pokemong/dob/{startDate}/{endDate}` to search for +`pokemongs` who where born within that interval (bounds included). + ## Prep steps ### ♨️Java version diff --git a/data/postman_collection.json b/data/postman_collection.json index 292f8cd..8ccb223 100644 --- a/data/postman_collection.json +++ b/data/postman_collection.json @@ -256,6 +256,49 @@ } }, "response": [] + }, + { + "name": "Get all pkmn by nickname", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8080/pokemong/nickname/sparky", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "pokemong", + "nickname", + "sparky" + ] + } + }, + "response": [] + }, + { + "name": "Gat all pkmn by date interval", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8080/pokemong/dob/1995-01-01/1999-01-01", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "pokemong", + "dob", + "1995-01-01", + "1999-01-01" + ] + } + }, + "response": [] } ] }, diff --git a/data/sample-dataset/pokemongs.json b/data/sample-dataset/pokemongs.json index 7ba88b9..df4fb06 100644 --- a/data/sample-dataset/pokemongs.json +++ b/data/sample-dataset/pokemongs.json @@ -6,7 +6,7 @@ "nickname": "Sparky", "dob": { "$date": { - "$numberLong": "761597551000" + "$numberLong": "861597551000" } }, "level": 15, diff --git a/data/sample-dataset/trainers.json b/data/sample-dataset/trainers.json index 913b61b..66c2712 100644 --- a/data/sample-dataset/trainers.json +++ b/data/sample-dataset/trainers.json @@ -6,7 +6,7 @@ "name": "Ash", "dob": { "$date": { - "$numberLong": "761598551000" + "$numberLong": "661598551000" } }, "wins": 100, @@ -34,7 +34,7 @@ "name": "Brock", "dob": { "$date": { - "$numberLong": "761596551000" + "$numberLong": "561596551000" } }, "wins": 70, diff --git a/src/main/java/fr/uca/iut/codecs/GenericCodec.java b/src/main/java/fr/uca/iut/codecs/GenericCodec.java index ec3898e..4c0d823 100644 --- a/src/main/java/fr/uca/iut/codecs/GenericCodec.java +++ b/src/main/java/fr/uca/iut/codecs/GenericCodec.java @@ -22,8 +22,8 @@ public abstract class GenericCodec implements Collectib * Encodes the entity into BSON. * This method must be overridden by subclasses. * - * @param writer The BsonWriter to write the BSON. - * @param entity The entity to encode. + * @param writer The BsonWriter to write the BSON. + * @param entity The entity to encode. * @param encoderContext The context for encoding. */ @Override @@ -79,7 +79,7 @@ public abstract class GenericCodec implements Collectib * Decodes a BSON document into an entity. * This method must be overridden by subclasses. * - * @param reader The BsonReader from which to read the BSON. + * @param reader The BsonReader from which to read the BSON. * @param decoderContext The context for decoding. * @return The decoded entity. */ diff --git a/src/main/java/fr/uca/iut/controllers/GenericController.java b/src/main/java/fr/uca/iut/controllers/GenericController.java index a215651..356dad3 100644 --- a/src/main/java/fr/uca/iut/controllers/GenericController.java +++ b/src/main/java/fr/uca/iut/controllers/GenericController.java @@ -88,7 +88,7 @@ public abstract class GenericController { /** * Updates an existing entity. * - * @param id The ID of the entity to update. + * @param id The ID of the entity to update. * @param entity The updated entity. * @return A Response object containing the updated entity, or an error message if the entity is not found or not valid. */ diff --git a/src/main/java/fr/uca/iut/controllers/PokemongController.java b/src/main/java/fr/uca/iut/controllers/PokemongController.java index 43f4000..a328a52 100644 --- a/src/main/java/fr/uca/iut/controllers/PokemongController.java +++ b/src/main/java/fr/uca/iut/controllers/PokemongController.java @@ -2,11 +2,18 @@ package fr.uca.iut.controllers; import fr.uca.iut.entities.Pokemong; import fr.uca.iut.services.PokemongService; +import fr.uca.iut.utils.StringUtils; import jakarta.annotation.PostConstruct; 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 jakarta.ws.rs.core.Response; + +import java.time.LocalDate; +import java.util.ArrayList; @Path("/pokemong") @Produces(MediaType.APPLICATION_JSON) @@ -19,4 +26,37 @@ public class PokemongController extends GenericController { public void init() { setService(pokemongService); } + + @GET + @Path("/nickname/{nickname}") + public Response findByName(@PathParam("nickname") String nickname) { + if (StringUtils.isBlankStringOrNull(nickname)) { + return Response.ok(new ArrayList<>()) + .build(); + } + return Response.ok(pokemongService.findByNickname(nickname.trim())) + .build(); + } + + @GET + @Path("/dob/{startDate}/{endDate}") + public Response findByDateOfBirthInterval( + @PathParam("startDate") String startDate, + @PathParam("endDate") String endDate + ) { + if (StringUtils.isBlankStringOrNull(startDate) || StringUtils.isBlankStringOrNull(endDate)) { + return Response.ok(new ArrayList<>()) + .build(); + } + try { + return Response + .ok(pokemongService.findByDateOfBirthInterval(LocalDate.parse(startDate), LocalDate.parse(endDate))) + .build(); + } catch (Exception e) { + return Response.status(Response.Status.BAD_REQUEST) + .entity(e.getMessage()) + .build(); + } + } + } diff --git a/src/main/java/fr/uca/iut/repositories/PokemongRepository.java b/src/main/java/fr/uca/iut/repositories/PokemongRepository.java index 703f60e..04c3851 100644 --- a/src/main/java/fr/uca/iut/repositories/PokemongRepository.java +++ b/src/main/java/fr/uca/iut/repositories/PokemongRepository.java @@ -11,7 +11,10 @@ import jakarta.inject.Inject; import org.bson.conversions.Bson; import org.bson.types.ObjectId; +import java.time.LocalDate; +import java.time.ZoneId; import java.util.ArrayList; +import java.util.Date; import java.util.List; @ApplicationScoped @@ -41,4 +44,37 @@ public class PokemongRepository extends GenericRepository { return db.getCollection(Pokemong.COLLECTION_NAME, Pokemong.class); } + /** + * Fetches the list of Pokemong entities that have a nickname matching the provided nickname. + * The match is case-insensitive and ignores leading and trailing spaces. + * If the nickname is null or empty, an empty list is returned. + * + * @param nickname the nickname to search for in the database. Can be a partial nickname. + * @return List of Pokemong entities with a nickname matching the provided nickname. If no match is found, an empty list is returned. + */ + public List findByNickname(String nickname) { + if (nickname != null) { + Bson filter = Filters.regex("nickname", nickname.trim(), "i"); + return getCollection().find(filter) + .into(new ArrayList<>()); + } + return new ArrayList<>(); + } + + /** + * Returns a list of Pokemongs born within the specified date interval. + * + * @param startDate the start of the date interval, inclusive + * @param endDate the end of the date interval, inclusive + * @return a list of Pokemongs born within the specified date interval + */ + public List findByDateOfBirthInterval(LocalDate startDate, LocalDate endDate) { + Bson filter = Filters.and( + Filters.gte("dob", Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant())), + Filters.lte("dob", Date.from(endDate.atStartOfDay(ZoneId.systemDefault()).toInstant())) + ); + return getCollection().find(filter) + .into(new ArrayList<>()); + } + } diff --git a/src/main/java/fr/uca/iut/services/PokemongService.java b/src/main/java/fr/uca/iut/services/PokemongService.java index 085d300..0178863 100644 --- a/src/main/java/fr/uca/iut/services/PokemongService.java +++ b/src/main/java/fr/uca/iut/services/PokemongService.java @@ -15,6 +15,7 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.jetbrains.annotations.NotNull; +import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -207,6 +208,10 @@ public class PokemongService extends GenericService { return repository.existsById(pokemongId); } + public List findByNickname(String nickname) { + return pokemongRepository.findByNickname(nickname); + } + public void batchUpdatePokemongTrainers(@NotNull Set trainerPokemongs, @Nullable String trainerId) { List pokemongsToUpdate = new ArrayList<>(); @@ -219,4 +224,9 @@ public class PokemongService extends GenericService { } updateAll(pokemongsToUpdate); } + + public List findByDateOfBirthInterval(LocalDate startDate, LocalDate endDate) { + return pokemongRepository.findByDateOfBirthInterval(startDate, endDate); + } + } diff --git a/src/main/resources/META-INF/openapi.yaml b/src/main/resources/META-INF/openapi.yaml index ba0cc4b..523e631 100644 --- a/src/main/resources/META-INF/openapi.yaml +++ b/src/main/resources/META-INF/openapi.yaml @@ -73,6 +73,58 @@ paths: '400': description: Invalid ID format + /pokemong/nickname/{nickname}: + + get: + summary: Get Pokemongs by nickname + parameters: + - name: nickname + in: path + required: true + description: The nickname of the Pokemong. It can be a partial nickname. The match is case-insensitive and ignores leading and trailing spaces. + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pokemong' + + /pokemong/dob/{startDate}/{endDate}: + + get: + summary: Get Pokemongs born within a certain date interval + parameters: + - name: startDate + in: path + required: true + description: The start of the date interval (inclusive) + schema: + type: string + format: date + - name: endDate + in: path + required: true + description: The end of the date interval (inclusive) + schema: + type: string + format: date + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pokemong' + '400': + description: Invalid date format + /pokemong: get: