From 95f041578198922ac5bb39b08eb6de7081b59e64 Mon Sep 17 00:00:00 2001 From: "alexis.drai@etu.uca.fr" Date: Tue, 20 Jun 2023 11:56:18 +0200 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=AB=B4=20Provide=20endpoint=20for=20g?= =?UTF-8?q?etting=20pokemongs=20by=20nickname?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++++++++++-- .../iut/controllers/PokemongController.java | 25 +++++++++++++++++++ .../iut/repositories/PokemongRepository.java | 18 +++++++++++++ .../fr/uca/iut/services/PokemongService.java | 4 +++ src/main/resources/META-INF/openapi.yaml | 21 ++++++++++++++++ 5 files changed, 84 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 82912ab..3847199 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,24 @@ 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 + ## Prep steps ### ♨️Java version diff --git a/src/main/java/fr/uca/iut/controllers/PokemongController.java b/src/main/java/fr/uca/iut/controllers/PokemongController.java index 43f4000..1273712 100644 --- a/src/main/java/fr/uca/iut/controllers/PokemongController.java +++ b/src/main/java/fr/uca/iut/controllers/PokemongController.java @@ -2,12 +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 java.util.ArrayList; +import java.util.List; + @Path("/pokemong") @Produces(MediaType.APPLICATION_JSON) public class PokemongController extends GenericController { @@ -19,4 +25,23 @@ public class PokemongController extends GenericController { public void init() { setService(pokemongService); } + + /** + * REST endpoint to fetch Pokemong entities by nickname. + * The match is case-insensitive, ignores leading and trailing spaces, and can be a partial nickname. + * If the nickname is null or blank, 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. + */ + @GET + @Path("/nickname/{nickname}") + public List findByName(@PathParam("nickname") String nickname) { + return StringUtils.isBlankStringOrNull(nickname) + ? new ArrayList<>() + : pokemongService.findByNickname(nickname.trim()); + } + + + } diff --git a/src/main/java/fr/uca/iut/repositories/PokemongRepository.java b/src/main/java/fr/uca/iut/repositories/PokemongRepository.java index 703f60e..f14b481 100644 --- a/src/main/java/fr/uca/iut/repositories/PokemongRepository.java +++ b/src/main/java/fr/uca/iut/repositories/PokemongRepository.java @@ -4,6 +4,7 @@ import com.mongodb.client.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.Filters; +import com.mongodb.lang.Nullable; import fr.uca.iut.entities.Pokemong; import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; @@ -41,4 +42,21 @@ 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<>(); + } + } diff --git a/src/main/java/fr/uca/iut/services/PokemongService.java b/src/main/java/fr/uca/iut/services/PokemongService.java index 085d300..ade729e 100644 --- a/src/main/java/fr/uca/iut/services/PokemongService.java +++ b/src/main/java/fr/uca/iut/services/PokemongService.java @@ -207,6 +207,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<>(); diff --git a/src/main/resources/META-INF/openapi.yaml b/src/main/resources/META-INF/openapi.yaml index ba0cc4b..d8d3736 100644 --- a/src/main/resources/META-INF/openapi.yaml +++ b/src/main/resources/META-INF/openapi.yaml @@ -73,6 +73,27 @@ 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: get: -- 2.36.3 From e6ead464a5a54d9da8fa735d91b36dae52e5ddb2 Mon Sep 17 00:00:00 2001 From: "alexis.drai@etu.uca.fr" Date: Tue, 20 Jun 2023 12:21:00 +0200 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=AB=B4=20Provide=20endpoint=20for=20g?= =?UTF-8?q?etting=20pokemongs=20by=20dob=20interval?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++ data/postman_collection.json | 43 +++++++++++++++++++ data/sample-dataset/pokemongs.json | 2 +- data/sample-dataset/trainers.json | 4 +- .../java/fr/uca/iut/codecs/GenericCodec.java | 6 +-- .../iut/controllers/GenericController.java | 2 +- .../iut/controllers/PokemongController.java | 43 +++++++++++++------ .../iut/repositories/PokemongRepository.java | 20 ++++++++- .../fr/uca/iut/services/PokemongService.java | 6 +++ src/main/resources/META-INF/openapi.yaml | 31 +++++++++++++ 10 files changed, 138 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 3847199..4c4932a 100644 --- a/README.md +++ b/README.md @@ -309,6 +309,9 @@ 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 1273712..a328a52 100644 --- a/src/main/java/fr/uca/iut/controllers/PokemongController.java +++ b/src/main/java/fr/uca/iut/controllers/PokemongController.java @@ -10,9 +10,10 @@ 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; -import java.util.List; @Path("/pokemong") @Produces(MediaType.APPLICATION_JSON) @@ -26,22 +27,36 @@ public class PokemongController extends GenericController { setService(pokemongService); } - /** - * REST endpoint to fetch Pokemong entities by nickname. - * The match is case-insensitive, ignores leading and trailing spaces, and can be a partial nickname. - * If the nickname is null or blank, 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. - */ @GET @Path("/nickname/{nickname}") - public List findByName(@PathParam("nickname") String nickname) { - return StringUtils.isBlankStringOrNull(nickname) - ? new ArrayList<>() - : pokemongService.findByNickname(nickname.trim()); + 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 f14b481..04c3851 100644 --- a/src/main/java/fr/uca/iut/repositories/PokemongRepository.java +++ b/src/main/java/fr/uca/iut/repositories/PokemongRepository.java @@ -4,7 +4,6 @@ import com.mongodb.client.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.Filters; -import com.mongodb.lang.Nullable; import fr.uca.iut.entities.Pokemong; import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; @@ -12,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 @@ -59,4 +61,20 @@ public class PokemongRepository extends GenericRepository { 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 ade729e..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; @@ -223,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 d8d3736..523e631 100644 --- a/src/main/resources/META-INF/openapi.yaml +++ b/src/main/resources/META-INF/openapi.yaml @@ -94,6 +94,37 @@ paths: 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: -- 2.36.3