🫴 Fix #9: Add specialized queries (#10)

Co-authored-by: alexis.drai@etu.uca.fr <alexis.drai@etu.uca.fr>
Reviewed-on: #10
pull/11/head
Alexis Drai 2 years ago
parent 7e4c3e5f11
commit 4147d840ea

@ -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

@ -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": []
}
]
},

@ -6,7 +6,7 @@
"nickname": "Sparky",
"dob": {
"$date": {
"$numberLong": "761597551000"
"$numberLong": "861597551000"
}
},
"level": 15,

@ -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,

@ -22,8 +22,8 @@ public abstract class GenericCodec<T extends GenericEntity> 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<T extends GenericEntity> 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.
*/

@ -88,7 +88,7 @@ public abstract class GenericController<T extends GenericEntity> {
/**
* 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.
*/

@ -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<Pokemong> {
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();
}
}
}

@ -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<Pokemong> {
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<Pokemong> 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<Pokemong> 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<>());
}
}

@ -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<Pokemong> {
return repository.existsById(pokemongId);
}
public List<Pokemong> findByNickname(String nickname) {
return pokemongRepository.findByNickname(nickname);
}
public void batchUpdatePokemongTrainers(@NotNull Set<TrainerPokemong> trainerPokemongs,
@Nullable String trainerId) {
List<Pokemong> pokemongsToUpdate = new ArrayList<>();
@ -219,4 +224,9 @@ public class PokemongService extends GenericService<Pokemong> {
}
updateAll(pokemongsToUpdate);
}
public List<Pokemong> findByDateOfBirthInterval(LocalDate startDate, LocalDate endDate) {
return pokemongRepository.findByDateOfBirthInterval(startDate, endDate);
}
}

@ -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:

Loading…
Cancel
Save