diff --git a/README.md b/README.md index 44eeee9ccb03540bca8a97f4209ab1d6ba239abf..6214179fe56246f3626f687fbc85e4c1ccafb395 100644 --- a/README.md +++ b/README.md @@ -25,29 +25,29 @@ curl -s -D - -X PUT "http://localhost:9998/myapp/biblio/init" Get author 1 in JSON ```shell curl -s -D - -H "Accept: application/json" \ - http://localhost:9998/myapp/biblio/auteurs/1 + http://localhost:9998/myapp/biblio/authors/1 ``` Get author 2 in XML ```shell curl -s -D - -H "Accept: text/xml" \ - http://localhost:9998/myapp/biblio/auteurs/2 + http://localhost:9998/myapp/biblio/authors/2 ``` Get authors in JSON ```shell curl -s -D - -H "Accept: application/json" \ - http://localhost:9998/myapp/biblio/auteurs + http://localhost:9998/myapp/biblio/authors ``` Removes an author ```shell -curl -s -D - -X DELETE "http://localhost:9998/myapp/biblio/auteurs/1" +curl -s -D - -X DELETE "http://localhost:9998/myapp/biblio/authors/1" ``` Removes all authors ```shell -curl -s -D - -X DELETE "http://localhost:9998/myapp/biblio/auteurs" +curl -s -D - -X DELETE "http://localhost:9998/myapp/biblio/authors" ``` Adds an author @@ -56,7 +56,7 @@ curl -s -D - -H "Accept: application/json" \ -H "Content-type: application/json" \ -X POST \ -d '{"nom":"John","prenom":"Smith","biographie":"My life"}' \ - "http://localhost:9998/myapp/biblio/auteurs/" + "http://localhost:9998/myapp/biblio/authors/" ``` Fully update an author @@ -65,25 +65,25 @@ curl -s -D - -H "Accept: application/json" \ -H "Content-type: application/json" \ -X PUT \ -d '{"nom":"Martin","prenom":"Jean","biographie":"ma vie"}' \ - "http://localhost:9998/myapp/biblio/auteurs/1" + "http://localhost:9998/myapp/biblio/authors/1" ``` If a resource doesn't exist an exception is raised, and the 404 http status code is returned ```shell curl -s -D - -H "Accept: application/json" \ - http://localhost:9998/myapp/biblio/auteurs/1000 + http://localhost:9998/myapp/biblio/authors/1000 ``` Filter resources with query parameters : ```shell curl -v -H "Accept: application/json" \ - "http://127.0.0.1:9998/myapp/biblio/auteurs/filter?nom=Durand&prenom⁼Marie" + "http://127.0.0.1:9998/myapp/biblio/authors/filter?nom=Durand&prenom⁼Marie" ``` Control sort key with header param (default value "nom") : ```shell curl -v -H "Accept: application/json" -H "sortKey: prenom"\ -"http://127.0.0.1:9998/myapp/biblio/auteurs/filter" +"http://127.0.0.1:9998/myapp/biblio/authors/filter" ``` Login and get a Java Web Token ```shell diff --git a/queries/sample-requests.rest b/queries/sample-requests.rest index bfbf655972055a7acf1cd1aeed2d4a0e14c82c6a..edf0eadaca9e16bf4109ae0e61ac523a460cd65a 100644 --- a/queries/sample-requests.rest +++ b/queries/sample-requests.rest @@ -1,101 +1,116 @@ -# curl -D - http://localhost:9998/myapp/biblio ### Get a Hello message -GET http://localhost:9998/myapp/biblio +GET http://localhost:9998/mylibrary/setup ### Init the database with two authors -PUT http://localhost:9998/myapp/biblio/init +PUT http://localhost:9998/mylibrary/setup/init ### Get author 1 in JSON -GET http://localhost:9998/myapp/biblio/auteurs/1 +GET http://localhost:9998/mylibrary/authors/1 Accept: application/json ### Get author 2 in XML -GET http://localhost:9998/myapp/biblio/auteurs/2 +GET http://localhost:9998/mylibrary/authors/2 Accept: text/xml +### Get Library in XML +GET http://localhost:9998/mylibrary/library +Accept: text/xml + +### Get Library in JSON +GET http://localhost:9998/mylibrary/library +Accept: application/json + ### Get authors in JSON -GET http://localhost:9998/myapp/biblio/auteurs +GET http://localhost:9998/mylibrary/authors Accept: application/json ### Removes an author -DELETE http://localhost:9998/myapp/biblio/auteurs/1 +DELETE http://localhost:9998/mylibrary/authors/1 ### Removes all authors -DELETE http://localhost:9998/myapp/biblio/auteurs +DELETE http://localhost:9998/mylibrary/authors ### Adds an author -POST http://localhost:9998/myapp/biblio/auteurs/ +POST http://localhost:9998/mylibrary/authors/ Accept: application/json Content-type: application/json -{"nom":"John","prenom":"Smith","biographie":"My life"} +{"name":"John","firstname":"Smith","biography":"My life"} + +### ReInit the database with two authors +PUT http://localhost:9998/mylibrary/setup/init ### Fully update an author -PUT http://localhost:9998/myapp/biblio/auteurs/1 +PUT http://localhost:9998/mylibrary/authors/1 Accept: application/json Content-type: application/json -{"nom":"Martin","prenom":"Jean","biographie":"ma vie"} +{"name":"Martin","firstname":"Jean","biography":"ma vie"} ### If a resource doesn't exist an exception is raised, and the 404 http status code is returned -GET http://localhost:9998/myapp/biblio/auteurs/1000 +GET http://localhost:9998/mylibrary/authors/1000 Accept: application/json ### If a resource doesn't exist an exception is raised, and the 404 http status code is returned -GET http://localhost:9998/myapp/biblio/auteurs/1000 +GET http://localhost:9998/mylibrary/authors/1000 Accept: text/xml ### Filter resources with query parameters : -GET http://localhost:9998/myapp/biblio/auteurs/filter?nom=Durand&prenom⁼Marie +GET http://localhost:9998/mylibrary/authors/filter?name=Durand&firstname⁼Marie Accept: application/json ### Control sort key with header param (default value "nom") : -GET http://127.0.0.1:9998/myapp/biblio/auteurs/filter +GET http://127.0.0.1:9998/mylibrary/authors/filter Accept: application/json -sortKey: prenom +sortKey: firstname ### Init the database with 10k random authors -PUT http://localhost:9998/myapp/biblio/init/10000 +PUT http://localhost:9998/mylibrary/setup/init/10000 ### Get page 3 with page size of 10 authors sorted by lastname -GET http://localhost:9998/myapp/biblio/auteurs/page?pageSize=10&page=3 +GET http://localhost:9998/mylibrary/authors/page?pageSize=10&page=3 Accept: application/json -sortKey: prenom +sortKey: firstname + +### Get page 3 with page size of 10 authors sorted by lastname +GET http://localhost:9998/mylibrary/authors/page?pageSize=10&page=3 +Accept: text/xml +sortKey: firstname ### Returns the context of the query (without authentication). -GET http://localhost:9998/myapp/biblio/context +GET http://localhost:9998/mylibrary/setup/context biblio-demo-header-1: myvalue biblio-demo-header-2: anothervalue ### Authorization by token, part 1. Retrieve and save token with Basic Authentication # TOKEN=$(curl -v --user "john.doe@nowhere.com:admin" "http://localhost:9998/myapp/biblio/login") -GET http://localhost:9998/myapp/biblio/login +GET http://localhost:9998/mylibrary/setup/login Authorization: Basic john.doe@nowhere.com admin > {% client.global.set("auth_token", response.body); %} ### Authorization by token, part 2. Use token to authorize. Admin & User OK # curl -H "Authorization: Bearer $TOKEN" -v "http://localhost:9998/myapp/biblio/secured" -GET http://localhost:9998/myapp/biblio/secured +GET http://localhost:9998/mylibrary/setup/secured Authorization: Bearer {{auth_token}} ### Authorization by token, part 2. Use token to authorize. Admin OK -GET http://localhost:9998/myapp/biblio/secured/admin +GET http://localhost:9998/mylibrary/setup/secured/admin Authorization: Bearer {{auth_token}} ### Authorization with another user. # TOKEN=$(curl -v --user "mary.roberts@here.net:user" "http://localhost:9998/myapp/biblio/login") -GET http://localhost:9998/myapp/biblio/login +GET http://localhost:9998/mylibrary/setup/login Authorization: Basic mary.roberts@here.net user > {% client.global.set("auth_token", response.body); %} ### Authorization by token, part 2. Use token to authorize. Admin & User OK. # curl -H "Authorization: Bearer $TOKEN" -v "http://localhost:9998/myapp/biblio/secured" -GET http://localhost:9998/myapp/biblio/secured +GET http://localhost:9998/mylibrary/setup/secured Authorization: Bearer {{auth_token}} ### Authorization by token, part 2. Use token to authorize. Admin KO. -GET http://localhost:9998/myapp/biblio/secured/admin +GET http://localhost:9998/mylibrary/setup/secured/admin Authorization: Bearer {{auth_token}} diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/client/BiblioClient.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/client/BiblioClient.java index 2af92d4717c498d5ee8be3b04eee194059278f16..79295d91ef754edc29fc1fc28a1695d71e6efb99 100644 --- a/src/main/java/fr/univtln/bruno/samples/jaxrs/client/BiblioClient.java +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/client/BiblioClient.java @@ -1,6 +1,6 @@ package fr.univtln.bruno.samples.jaxrs.client; -import fr.univtln.bruno.samples.jaxrs.model.BiblioModel.Auteur; +import fr.univtln.bruno.samples.jaxrs.model.Library.Author; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; import jakarta.ws.rs.client.Entity; @@ -15,7 +15,7 @@ import lombok.extern.java.Log; @Log public class BiblioClient { public static void main(String[] args) { - // create the client + // create the rest client Client client = ClientBuilder.newClient(); WebTarget webResource = client.target("http://localhost:9998/myapp"); @@ -25,15 +25,15 @@ public class BiblioClient { log.info(responseInitAsString); //Send a get and parse the response as a String - String responseAuteursAsJsonString = webResource.path("biblio/auteurs") + String responseAuteursAsJsonString = webResource.path("biblio/authors") .request().get(String.class); log.info(responseAuteursAsJsonString); //Idem but the result is deserialised to an instance of Auteur - Auteur auteur = webResource.path("biblio/auteurs/1") + Author author = webResource.path("biblio/authors/1") .request() - .get(Auteur.class); - log.info(auteur.toString()); + .get(Author.class); + log.info(author.toString()); //Log in to get the token with basci authentication String email = "john.doe@nowhere.com"; diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/exceptions/BusinessException.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/exceptions/BusinessException.java index fa83532d978ffef37ada52f7627f791b2261cc70..520164771bf4dd02780efa17af37e1866a2e4fb4 100644 --- a/src/main/java/fr/univtln/bruno/samples/jaxrs/exceptions/BusinessException.java +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/exceptions/BusinessException.java @@ -9,7 +9,8 @@ import lombok.Getter; import java.io.Serializable; /** - * The type Business exception, used add HTTP (HATEOS) capacities to exceptions. + * The type Business exception is our main HATEOS exception type. + * It adds a HTTP Status. */ @Getter @JsonIgnoreProperties({"stackTrace"}) @@ -23,7 +24,7 @@ public class BusinessException extends Exception implements Serializable { final Response.Status status; /** - * Instantiates a new Business exception. + * Instantiates a new Business exception with the default message. * * @param status the status */ @@ -31,4 +32,15 @@ public class BusinessException extends Exception implements Serializable { super(status.getReasonPhrase()); this.status = status; } + + /** + * Instantiates a new Business exception with the default message. + * + * @param status the status + * @param message the message + */ + public BusinessException(Response.Status status,String message) { + super(message); + this.status = status; + } } diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/exceptions/IllegalArgumentException.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/exceptions/IllegalArgumentException.java index deae099b7181c328ecf1bdccf0857166e424c4f5..fb6f5ba80968e16888f44e9ca1afd5ac6dd4330e 100644 --- a/src/main/java/fr/univtln/bruno/samples/jaxrs/exceptions/IllegalArgumentException.java +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/exceptions/IllegalArgumentException.java @@ -1,7 +1,10 @@ package fr.univtln.bruno.samples.jaxrs.exceptions; +import jakarta.xml.bind.annotation.XmlRootElement; + import static jakarta.ws.rs.core.Response.Status; +@XmlRootElement public class IllegalArgumentException extends BusinessException { public IllegalArgumentException() { super(Status.NOT_ACCEPTABLE); diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/mappers/BusinessExceptionMapper.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/mappers/BusinessExceptionMapper.java index a057821d46434c40ee0559cfff548a73baf205e5..e3f97cebbaeddf0f8083ee18d8e9fa81247b4347 100755 --- a/src/main/java/fr/univtln/bruno/samples/jaxrs/mappers/BusinessExceptionMapper.java +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/mappers/BusinessExceptionMapper.java @@ -8,6 +8,10 @@ import lombok.AccessLevel; import lombok.experimental.FieldDefaults; import lombok.extern.java.Log; +/** + * The type Business exception mapper automatically produces a HTTP Response + * if a BusinessException is thrown. + */ @SuppressWarnings("unused") @Provider @FieldDefaults(level = AccessLevel.PRIVATE) diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/mappers/GenericExceptionMapper.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/mappers/GenericExceptionMapper.java index 554394ad53ee603c1dc9867a5914e7cc077b3c7a..49392aae3bc86b752a22e3c26804e37a76fe54c6 100755 --- a/src/main/java/fr/univtln/bruno/samples/jaxrs/mappers/GenericExceptionMapper.java +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/mappers/GenericExceptionMapper.java @@ -1,5 +1,6 @@ package fr.univtln.bruno.samples.jaxrs.mappers; +import fr.univtln.bruno.samples.jaxrs.exceptions.BusinessException; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.ext.ExceptionMapper; @@ -8,14 +9,19 @@ import lombok.AccessLevel; import lombok.experimental.FieldDefaults; import lombok.extern.java.Log; +/** + * The type Generic exception mapper automatically produces a HTTP Response + * if a Exception is thrown with a default status (500). + */ @SuppressWarnings("unused") @Provider @FieldDefaults(level = AccessLevel.PRIVATE) @Log public class GenericExceptionMapper implements ExceptionMapper<Exception> { - public Response toResponse(Exception ex) { + public Response toResponse(Exception exception) { + log.info("--->"+exception.toString()); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) - .entity(ex.getMessage()) + .entity(new BusinessException(Response.Status.INTERNAL_SERVER_ERROR, exception.getMessage())) .type(MediaType.APPLICATION_JSON) .build(); } diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/model/BiblioModel.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/model/BiblioModel.java deleted file mode 100644 index ff787698d1e8fa944a70ac3e659936b5bf855078..0000000000000000000000000000000000000000 --- a/src/main/java/fr/univtln/bruno/samples/jaxrs/model/BiblioModel.java +++ /dev/null @@ -1,185 +0,0 @@ -package fr.univtln.bruno.samples.jaxrs.model; - -import fr.univtln.bruno.samples.jaxrs.exceptions.IllegalArgumentException; -import fr.univtln.bruno.samples.jaxrs.exceptions.NotFoundException; -import fr.univtln.bruno.samples.jaxrs.resources.PaginationInfo; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlAttribute; -import jakarta.xml.bind.annotation.XmlRootElement; -import lombok.*; -import lombok.experimental.FieldDefaults; -import lombok.extern.java.Log; -import org.eclipse.collections.api.map.primitive.MutableLongObjectMap; -import org.eclipse.collections.impl.factory.primitive.LongObjectMaps; - -import java.io.Serializable; -import java.security.InvalidParameterException; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static fr.univtln.bruno.samples.jaxrs.model.BiblioModel.Field.valueOf; - - -/** - * The type Biblio model. A in memory instance of a Library model. Kind of a mock. - */ -@Log -@Getter -@FieldDefaults(level = AccessLevel.PRIVATE) -@NoArgsConstructor(staticName = "of") -public class BiblioModel { - private static final AtomicLong lastId = new AtomicLong(0); - //@Delegate - final MutableLongObjectMap<Auteur> auteurs = LongObjectMaps.mutable.empty(); - - /** - * Add auteur auteur. - * - * @param auteur the auteur - * @return the auteur - * @throws IllegalArgumentException the illegal argument exception - */ - public Auteur addAuteur(Auteur auteur) throws IllegalArgumentException { - if (auteur.id != 0) throw new IllegalArgumentException(); - auteur.id = lastId.incrementAndGet(); - auteurs.put(auteur.id, auteur); - return auteur; - } - - /** - * Update auteur auteur by id and data contains in a author instance (except the id). - * - * @param id the id - * @param auteur the auteur - * @return the auteur - * @throws NotFoundException the not found exception - * @throws IllegalArgumentException the illegal argument exception - */ - public Auteur updateAuteur(long id, Auteur auteur) throws NotFoundException, IllegalArgumentException { - if (auteur.id != 0) throw new IllegalArgumentException(); - auteur.id = id; - if (!auteurs.containsKey(id)) throw new NotFoundException(); - auteurs.put(id, auteur); - return auteur; - } - - /** - * Remove one auteur by id. - * - * @param id the id - * @throws NotFoundException the not found exception - */ - public void removeAuteur(long id) throws NotFoundException { - if (!auteurs.containsKey(id)) throw new NotFoundException(); - auteurs.remove(id); - } - - /** - * Gets one auteur id. - * - * @param id the id - * @return the auteur - * @throws NotFoundException the not found exception - */ - public Auteur getAuteur(long id) throws NotFoundException { - if (!auteurs.containsKey(id)) throw new NotFoundException(); - return auteurs.get(id); - } - - /** - * Gets the number of authors. - * - * @return the auteur size - */ - public int getAuteurSize() { - return auteurs.size(); - } - - /** - * Returns a sorted, filtered and paginated list of authors. - * - * @param paginationInfo the pagination info - * @return the sorted, filtered page. - */ - public List<Auteur> getWithFilter(PaginationInfo paginationInfo) { - //We build a author stream, first we add sorting - Stream<Auteur> auteurStream = auteurs.stream() - .sorted(Comparator.comparing(auteur -> switch (valueOf(paginationInfo.getSortKey().toUpperCase())) { - case NOM -> auteur.getNom(); - case PRENOM -> auteur.getPrenom(); - default -> throw new InvalidParameterException(); - })); - - //The add filters according to parameters - if (paginationInfo.getNom() != null) - auteurStream = auteurStream.filter(auteur -> auteur.getNom().equalsIgnoreCase(paginationInfo.getNom())); - if (paginationInfo.getPrenom() != null) - auteurStream = auteurStream.filter(auteur -> auteur.getPrenom().equalsIgnoreCase(paginationInfo.getPrenom())); - if (paginationInfo.getBiographie() != null) - auteurStream = auteurStream.filter(auteur -> auteur.getBiographie().contains(paginationInfo.getBiographie())); - - //Finally add pagination instructions. - if ((paginationInfo.getPage() > 0) && (paginationInfo.getPageSize() > 0)) { - auteurStream = auteurStream - .skip(paginationInfo.getPageSize() * (paginationInfo.getPage() - 1)) - .limit(paginationInfo.getPageSize()); - } - - return auteurStream.collect(Collectors.toList()); - } - - /** - * Removes all authors. - */ - public void supprimerAuteurs() { - auteurs.clear(); - lastId.set(0); - } - - /** - * The list of fields of author that can used in filters. - */ - public enum Field { - NOM, - /** - * Prenom field. - */ - PRENOM, - /** - * Biographie field. - */ - BIOGRAPHIE - } - - /** - * The type Author. - */ - @Builder - @Getter - @Setter - @FieldDefaults(level = AccessLevel.PRIVATE) - @NoArgsConstructor - @AllArgsConstructor - - //To enable XML Serialization - @XmlRootElement - //Because getter are generated by lombok - @XmlAccessorType(XmlAccessType.FIELD) - @EqualsAndHashCode(onlyExplicitlyIncluded = true) - @ToString - public static class Auteur implements Serializable { - @XmlAttribute - @EqualsAndHashCode.Include - long id; - - String nom; - - String prenom; - - String biographie; - } -} diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/model/Library.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/model/Library.java new file mode 100644 index 0000000000000000000000000000000000000000..0b5410257d7bea05f6a75ccef1cb1496848b38d3 --- /dev/null +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/model/Library.java @@ -0,0 +1,282 @@ +package fr.univtln.bruno.samples.jaxrs.model; + +import com.fasterxml.jackson.annotation.*; +import fr.univtln.bruno.samples.jaxrs.exceptions.BusinessException; +import fr.univtln.bruno.samples.jaxrs.exceptions.NotFoundException; +import fr.univtln.bruno.samples.jaxrs.resources.PaginationInfo; +import jakarta.ws.rs.core.Response; +import jakarta.xml.bind.annotation.*; +import lombok.*; +import lombok.experimental.FieldDefaults; +import lombok.extern.java.Log; +import org.eclipse.collections.api.map.primitive.MutableLongObjectMap; +import org.eclipse.collections.impl.factory.primitive.LongObjectMaps; + +import java.io.Serializable; +import java.security.InvalidParameterException; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static fr.univtln.bruno.samples.jaxrs.model.Library.Field.valueOf; + + +/** + * The type Biblio model. A in memory instance of a Library model. Kind of a mock. + */ +@Log +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) +@NoArgsConstructor(staticName = "newInstance") +@XmlRootElement +public class Library { + //An in memory instance of a Library model. Kind of a mock. + public static final Library demoLibrary = Library.newInstance(); + + private static final AtomicLong lastAuthorId = new AtomicLong(0); + private static final AtomicLong lastBookId = new AtomicLong(0); + + @JsonIgnore + final MutableLongObjectMap<Author> authors = LongObjectMaps.mutable.empty(); + + /** + * used mainly to provide easy XML Serialization + * @return the list of authors + */ + @XmlElementWrapper(name = "authors") + @XmlElements({@XmlElement(name = "author")}) + @JsonProperty("authors") + public List<Author> getAuthorsAsList() { + return authors.toList(); + } + /** + * used mainly to provide easy XML Serialization + * @return the list of books + */ + @XmlElementWrapper(name = "books") + @XmlElements({@XmlElement(name = "book")}) + @JsonProperty("books") + public List<Book> getBooksAsList() { + return books.toList(); + } + + final MutableLongObjectMap<Book> books = LongObjectMaps.mutable.empty(); + + /** + * Adds an author to the model. + * + * @param author the author without an id. + * @return the author with its id. + * @throws BusinessException if the format is incorrect. + */ + public Author addAuthor(Author author) throws BusinessException { + if (author.id != 0) throw new BusinessException(Response.Status.INTERNAL_SERVER_ERROR, "Id shouldn't be given"); + + author.id = lastAuthorId.incrementAndGet(); + authors.put(author.id, author); + return author; + } + + /** + * Adds a book to the model + * + * @param book the book without its id and an non empty authors set. + * @return the book with its id. + * @throws BusinessException if the format is incorrect. + */ + public Book addBook(Book book) throws BusinessException { + if (book.id != 0) throw new BusinessException(Response.Status.INTERNAL_SERVER_ERROR, "Id shouldn't be given"); + if (book.authors == null || book.authors.isEmpty()) + throw new BusinessException(Response.Status.INTERNAL_SERVER_ERROR, "Author set is mandatory"); + book.id = lastAuthorId.incrementAndGet(); + book.authors.stream().forEach(auteur -> { + if (auteur.books == null) auteur.books = new HashSet<>(); + auteur.books.add(book); + }); + books.put(book.id, book); + return book; + } + + /** + * Updates auteur by id with data contained in an author instance (without id). + * + * @param id the id of the author to update + * @param author the author instance containing the data without id. + * @return the update author + * @throws BusinessException if the author if not found or the data invalid. + */ + public Author updateAuteur(long id, Author author) throws BusinessException { + if (author.id != 0) + throw new BusinessException(Response.Status.INTERNAL_SERVER_ERROR, "Id shouldn't be given in data"); + author.id = id; + if (!authors.containsKey(id)) throw new BusinessException(Response.Status.NOT_FOUND, "Author not found"); + authors.put(id, author); + return author; + } + + /** + * Removes one auteur by id. + * + * @param id the id + * @throws BusinessException if not found + */ + public void removeAuthor(long id) throws BusinessException { + if (!authors.containsKey(id)) throw new BusinessException(Response.Status.NOT_FOUND, "Author not found"); + authors.remove(id); + } + + /** + * Gets one auteur id. + * + * @param id the id + * @return the author + * @throws NotFoundException if not found exception + */ + public Author getAuthor(long id) throws BusinessException { + if (!authors.containsKey(id)) throw new BusinessException(Response.Status.NOT_FOUND, "Author not found"); + return authors.get(id); + } + + /** + * Gets the number of authors. + * + * @return the number of authors + */ + @JsonIgnore + public int getAuthorsNumber() { + return authors.size(); + } + + /** + * Returns a sorted, filtered and paginated list of authors. + * + * @param paginationInfo the pagination info + * @return the sorted, filtered page. + */ + public List<Author> getAuthorsWithFilter(PaginationInfo paginationInfo) { + //We build a author stream, first we add sorting + Stream<Author> authorStream = authors.stream() + .sorted(Comparator.comparing(auteur -> switch (valueOf(paginationInfo.getSortKey().toUpperCase())) { + case NAME -> auteur.getName(); + case FIRSTNAME -> auteur.getFirstname(); + default -> throw new InvalidParameterException(); + })); + + //The add filters according to parameters + if (paginationInfo.getName() != null) + authorStream = authorStream.filter(author -> author.getName().equalsIgnoreCase(paginationInfo.getName())); + if (paginationInfo.getFirstname() != null) + authorStream = authorStream.filter(author -> author.getFirstname().equalsIgnoreCase(paginationInfo.getFirstname())); + if (paginationInfo.getBiography() != null) + authorStream = authorStream.filter(author -> author.getBiography().contains(paginationInfo.getBiography())); + + //Finally add pagination instructions. + if ((paginationInfo.getPage() > 0) && (paginationInfo.getPageSize() > 0)) { + authorStream = authorStream + .skip(paginationInfo.getPageSize() * (paginationInfo.getPage() - 1)) + .limit(paginationInfo.getPageSize()); + } + + return authorStream.collect(Collectors.toList()); + } + + /** + * Removes all authors. + */ + public void removesAuthors() { + authors.clear(); + lastAuthorId.set(0); + } + + /** + * The list of fields of author that can used in filters. + */ + public enum Field { + NAME, + FIRSTNAME, + BIOGRAPHY + } + + /** + * The type Author. + */ + @Builder + @Getter + @Setter + @FieldDefaults(level = AccessLevel.PRIVATE) + @NoArgsConstructor + @AllArgsConstructor + + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIdentityInfo( + generator = ObjectIdGenerators.PropertyGenerator.class, + property = "id") + + //To enable XML Serialization + @XmlRootElement + //Because getter are generated by lombok + @XmlAccessorType(XmlAccessType.FIELD) + @EqualsAndHashCode(onlyExplicitlyIncluded = true) + @ToString + public static class Author implements Serializable { + @EqualsAndHashCode.Include + @XmlTransient + long id; + String name; + String firstname; + String biography; + @XmlIDREF + @XmlElementWrapper(name = "books") + @XmlElements({@XmlElement(name = "book")}) + @JsonIdentityReference(alwaysAsId = true) + Set<Book> books; + + @XmlID + @XmlAttribute(name = "id") + private String getXmlID() { + return String.format("%s-%s", this.getClass().getSimpleName(), this.getId()); + } + } + + @Builder + @Getter + @Setter + @FieldDefaults(level = AccessLevel.PRIVATE) + @NoArgsConstructor + @AllArgsConstructor + + @JsonIdentityInfo( + generator = ObjectIdGenerators.PropertyGenerator.class, + property = "id") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + + //To enable XML Serialization + @XmlRootElement + //Because getter are generated by lombok + @XmlAccessorType(XmlAccessType.FIELD) + + @EqualsAndHashCode(onlyExplicitlyIncluded = true) + @ToString + public static class Book implements Serializable { + @EqualsAndHashCode.Include + @XmlTransient + long id; + String title; + @XmlIDREF + @XmlElementWrapper(name = "authors") + @XmlElements({@XmlElement(name = "author")}) + @JsonIdentityReference(alwaysAsId = true) + Set<Author> authors; + + @XmlID + @XmlAttribute(name = "id") + private String getXmlID() { + return String.format("%s-%s", this.getClass().getSimpleName(), this.getId()); + } + + } +} diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/model/package-info.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/model/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..abb28a295c51205d95b109109ed94920d8de035d --- /dev/null +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/model/package-info.java @@ -0,0 +1,14 @@ +@XmlSchema( + xmlns = { + @XmlNs(prefix="ebjax", namespaceURI="http://bruno.univ-tln.fr/sample-jaxrs"), + @XmlNs(prefix="xsd", namespaceURI="http://www.w3.org/2001/XMLSchema") + }, + namespace="http://bruno.univ-tln.fr/sample-jaxrs", + elementFormDefault= XmlNsForm.UNQUALIFIED, + attributeFormDefault=XmlNsForm.UNQUALIFIED +) +package fr.univtln.bruno.samples.jaxrs.model; + +import jakarta.xml.bind.annotation.XmlNs; +import jakarta.xml.bind.annotation.XmlNsForm; +import jakarta.xml.bind.annotation.XmlSchema; \ No newline at end of file diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/BiblioResource.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/AdminResource.java similarity index 58% rename from src/main/java/fr/univtln/bruno/samples/jaxrs/resources/BiblioResource.java rename to src/main/java/fr/univtln/bruno/samples/jaxrs/resources/AdminResource.java index 8cafd62722b53e0a344f08d651dc57478c7e933b..00d2b3705491be08b3560ba6662fea3a8f773eba 100644 --- a/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/BiblioResource.java +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/AdminResource.java @@ -1,15 +1,15 @@ package fr.univtln.bruno.samples.jaxrs.resources; +import fr.univtln.bruno.samples.jaxrs.exceptions.BusinessException; import fr.univtln.bruno.samples.jaxrs.exceptions.IllegalArgumentException; -import fr.univtln.bruno.samples.jaxrs.exceptions.NotFoundException; -import fr.univtln.bruno.samples.jaxrs.model.BiblioModel; -import fr.univtln.bruno.samples.jaxrs.model.BiblioModel.Auteur; +import fr.univtln.bruno.samples.jaxrs.model.Library; +import fr.univtln.bruno.samples.jaxrs.model.Library.Author; +import fr.univtln.bruno.samples.jaxrs.model.Library.Book; import fr.univtln.bruno.samples.jaxrs.security.InMemoryLoginModule; import fr.univtln.bruno.samples.jaxrs.security.User; import fr.univtln.bruno.samples.jaxrs.security.annotations.BasicAuth; import fr.univtln.bruno.samples.jaxrs.security.annotations.JWTAuth; -import fr.univtln.bruno.samples.jaxrs.status.Status; import io.jsonwebtoken.Jwts; import jakarta.annotation.security.RolesAllowed; import jakarta.ws.rs.*; @@ -21,9 +21,9 @@ import java.security.SecureRandom; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.temporal.ChronoUnit; -import java.util.Collection; +import java.util.Collections; import java.util.Date; -import java.util.List; +import java.util.Set; /** * The Biblio resource. @@ -31,11 +31,9 @@ import java.util.List; */ @Log // The Java class will be hosted at the URI path "/biblio" -@Path("biblio") +@Path("setup") @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) -public class BiblioResource { - //A in memory instance of a Library model. Kind of a mock. - private static final BiblioModel modeleBibliotheque = BiblioModel.of(); +public class AdminResource { //A random number generator private static final SecureRandom random = new SecureRandom(); @@ -60,11 +58,17 @@ public class BiblioResource { */ @PUT @Path("init") - public int init() throws IllegalArgumentException { - modeleBibliotheque.supprimerAuteurs(); - modeleBibliotheque.addAuteur(Auteur.builder().prenom("Alfred").nom("Martin").build()); - modeleBibliotheque.addAuteur(Auteur.builder().prenom("Marie").nom("Durand").build()); - return modeleBibliotheque.getAuteurSize(); + public int init() throws BusinessException { + Library.demoLibrary.removesAuthors(); + Author author1 = Library.demoLibrary.addAuthor(Library.Author.builder().firstname("Alfred").name("Martin").build()); + Library.Author author2 = Library.demoLibrary.addAuthor(Author.builder().firstname("Marie").name("Durand").build()); + + Library.demoLibrary.addBook(Book.builder().title("title1").authors(Set.of(author1)).build()); + Library.demoLibrary.addBook(Book.builder().title("title2").authors(Set.of(author1, author2)).build()); + Library.demoLibrary.addBook(Book.builder().title("title3").authors(Set.of(author2)).build()); + Library.demoLibrary.addBook(Book.builder().title("title4").authors(Set.of(author2)).build()); + + return Library.demoLibrary.getAuthorsNumber(); } /** @@ -78,14 +82,14 @@ public class BiblioResource { */ @PUT @Path("init/{size:[0-9]+}") - public int init(@PathParam("size") int size) throws IllegalArgumentException { - modeleBibliotheque.supprimerAuteurs(); + public int init(@PathParam("size") int size) throws BusinessException { + Library.demoLibrary.removesAuthors(); for (int i = 0; i < size; i++) - modeleBibliotheque.addAuteur( - Auteur.builder() - .prenom(randomString(random.nextInt(6) + 2)) - .nom(randomString(random.nextInt(6) + 2)).build()); - return modeleBibliotheque.getAuteurSize(); + Library.demoLibrary.addAuthor( + Author.builder() + .firstname(randomString(random.nextInt(6) + 2)) + .name(randomString(random.nextInt(6) + 2)).build()); + return Library.demoLibrary.getAuthorsNumber(); } /** @@ -103,120 +107,6 @@ public class BiblioResource { .toString(); } - /** - * Update an author with an given id. - * - * @param id the id injected from the path param "id" - * @param auteur a injected author made from the JSON data (@Consumes) from body of the request. This author is forbidden to havce an Id. - * @return The resulting author with its id. - * @throws NotFoundException is returned if no author has the "id". - * @throws IllegalArgumentException is returned if an "id" is also given in the request body. - */ - @PUT - @Path("auteurs/{id}") - @Consumes(MediaType.APPLICATION_JSON) - public Auteur updateAuteur(@PathParam("id") long id, Auteur auteur) throws NotFoundException, IllegalArgumentException { - return modeleBibliotheque.updateAuteur(id, auteur); - } - - /** - * Adds an new author to the data. - * Status annotation is a trick to fine tune 2XX status codes (see the status package). - * - * @param auteur The author to be added without its id. - * @return The added author with its id. - * @throws IllegalArgumentException if the author has an explicit id (id!=0). - */ - @POST - @Status(Status.CREATED) - @Path("auteurs") - @Consumes(MediaType.APPLICATION_JSON) - public Auteur ajouterAuteur(Auteur auteur) throws IllegalArgumentException { - return modeleBibliotheque.addAuteur(auteur); - } - - /** - * Removes an author by id from the data. - * - * @param id the id of the author to remove - * @throws NotFoundException is returned if no author has the "id". - */ - @DELETE - @Path("auteurs/{id}") - public void supprimerAuteur(@PathParam("id") final long id) throws NotFoundException { - modeleBibliotheque.removeAuteur(id); - } - - /** - * Removes every authors - */ - @DELETE - @Path("auteurs") - public void supprimerAuteurs() { - modeleBibliotheque.supprimerAuteurs(); - } - - /** - * Find and return an author by id with a GET on the path "biblio/auteurs/{id}" where {id} is the needed id. - * The path parameter "id" is injected with @PathParam. - * - * @param id the needed author id. - * @return the auteur with id. - * @throws NotFoundException is returned if no author has the "id". - */ - @GET - @Path("auteurs/{id}") - public Auteur getAuteur(@PathParam("id") final long id) throws NotFoundException { - return modeleBibliotheque.getAuteur(id); - } - - /** - * Gets auteurs. - * - * @return the auteurs - */ - @GET - @Path("auteurs") - public Collection<Auteur> getAuteurs() { - return modeleBibliotheque.getAuteurs().values(); - } - - /** - * Gets a list of "filtered" authors. - * - * @param nom an optional exact filter on the name. - * @param prenom an optional exact filter on the firstname. - * @param biographie an optional contains filter on the biography. - * @param sortKey the sort key (prenom or nom). - * @return the filtered auteurs - */ - @GET - @Path("auteurs/filter") - public List<Auteur> getFilteredAuteurs(@QueryParam("nom") String nom, - @QueryParam("prenom") String prenom, - @QueryParam("biographie") String biographie, - @HeaderParam("sortKey") @DefaultValue("nom") String sortKey) { - PaginationInfo paginationInfo = PaginationInfo.builder() - .nom(nom) - .prenom(prenom) - .biographie(biographie) - .sortKey(sortKey) - .build(); - log.info(paginationInfo.toString()); - return modeleBibliotheque.getWithFilter(paginationInfo); - } - - /** - * Gets a page of authors after applying a sort. - * - * @param paginationInfo the pagination info represented as a class injected with @BeanParam. - * @return the page of authors. - */ - @GET - @Path("auteurs/page") - public List<Auteur> getAuteursPage(@BeanParam PaginationInfo paginationInfo) { - return modeleBibliotheque.getWithFilter(paginationInfo); - } /** * A GET method to access the context of the request : The URI, the HTTP headers, the request and the security context (needs authentication see below). diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/AuthorRessource.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/AuthorRessource.java new file mode 100644 index 0000000000000000000000000000000000000000..9e5daf9f7b81c247a16e881f4b587c3dbffbb6fa --- /dev/null +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/AuthorRessource.java @@ -0,0 +1,131 @@ +package fr.univtln.bruno.samples.jaxrs.resources; + +import fr.univtln.bruno.samples.jaxrs.exceptions.BusinessException; +import fr.univtln.bruno.samples.jaxrs.exceptions.IllegalArgumentException; +import fr.univtln.bruno.samples.jaxrs.exceptions.NotFoundException; +import fr.univtln.bruno.samples.jaxrs.model.Library; +import fr.univtln.bruno.samples.jaxrs.status.Status; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import lombok.extern.java.Log; + +import java.util.Collection; +import java.util.List; + +@Log +@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) +@Path("authors") +public class AuthorRessource { + /** + * Update an author with an given id. + * + * @param id the id injected from the path param "id" + * @param author a injected author made from the JSON data (@Consumes) from body of the request. This author is forbidden to havce an Id. + * @return The resulting author with its id. + * @throws NotFoundException is returned if no author has the "id". + * @throws IllegalArgumentException is returned if an "id" is also given in the request body. + */ + @PUT + @Path("{id}") + @Consumes(MediaType.APPLICATION_JSON) + public Library.Author updateAuteur(@PathParam("id") long id, Library.Author author) throws BusinessException { + return Library.demoLibrary.updateAuteur(id, author); + } + + /** + * Adds an new author to the data. + * Status annotation is a trick to fine tune 2XX status codes (see the status package). + * + * @param author The author to be added without its id. + * @return The added author with its id. + * @throws IllegalArgumentException if the author has an explicit id (id!=0). + */ + @POST + @Status(Status.CREATED) + @Consumes(MediaType.APPLICATION_JSON) + public Library.Author ajouterAuteur(Library.Author author) throws BusinessException { + return Library.demoLibrary.addAuthor(author); + } + + /** + * Removes an author by id from the data. + * + * @param id the id of the author to remove + * @throws NotFoundException is returned if no author has the "id". + */ + @DELETE + @Path("{id}") + public void supprimerAuteur(@PathParam("id") final long id) throws BusinessException { + Library.demoLibrary.removeAuthor(id); + } + + /** + * Removes every authors + */ + @DELETE + public void supprimerAuteurs() { + Library.demoLibrary.removesAuthors(); + } + + /** + * Find and return an author by id with a GET on the path "biblio/auteurs/{id}" where {id} is the needed id. + * The path parameter "id" is injected with @PathParam. + * + * @param id the needed author id. + * @return the auteur with id. + * @throws NotFoundException is returned if no author has the "id". + */ + @GET + @Path("{id}") + public Library.Author getAuteur(@PathParam("id") final long id) throws BusinessException { + return Library.demoLibrary.getAuthor(id); + } + + /** + * Gets auteurs. + * + * @return the auteurs + */ + @GET + public Collection<Library.Author> getAuteurs() { + return Library.demoLibrary.getAuthors().values(); + } + + /** + * Gets a list of "filtered" authors. + * + * @param name an optional exact filter on the name. + * @param firstname an optional exact filter on the firstname. + * @param biography an optional contains filter on the biography. + * @param sortKey the sort key (prenom or nom). + * @return the filtered auteurs + */ + @GET + @Path("filter") + public List<Library.Author> getFilteredAuteurs(@QueryParam("name") String name, + @QueryParam("firstname") String firstname, + @QueryParam("biography") String biography, + @HeaderParam("sortKey") @DefaultValue("name") String sortKey) { + PaginationInfo paginationInfo = PaginationInfo.builder() + .name(name) + .firstname(firstname) + .biography(biography) + .sortKey(sortKey) + .build(); + log.info(paginationInfo.toString()); + return Library.demoLibrary.getAuthorsWithFilter(paginationInfo); + } + + /** + * Gets a page of authors after applying a sort. + * + * @param paginationInfo the pagination info represented as a class injected with @BeanParam. + * @return the page of authors. + */ + @GET + @Path("page") + public List<Library.Author> getAuteursPage(@BeanParam PaginationInfo paginationInfo) { + return Library.demoLibrary.getAuthorsWithFilter(paginationInfo); + } + +} diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/LibraryRessource.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/LibraryRessource.java new file mode 100644 index 0000000000000000000000000000000000000000..b12c599f9239c72797ef219ac2bf1dc20bd7af82 --- /dev/null +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/LibraryRessource.java @@ -0,0 +1,18 @@ +package fr.univtln.bruno.samples.jaxrs.resources; + +import fr.univtln.bruno.samples.jaxrs.model.Library; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import lombok.extern.java.Log; + +@Log +@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) +@Path("library") +public class LibraryRessource { + @GET + public Library getAuteurs() { + return Library.demoLibrary; + } +} diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/PaginationInfo.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/PaginationInfo.java index bc9c399dccbe71390b404529edbe4d86edbba419..20b357403dde68adaaf2236a41580d29b5c69b0e 100644 --- a/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/PaginationInfo.java +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/resources/PaginationInfo.java @@ -28,15 +28,15 @@ public class PaginationInfo { long pageSize = 10; @HeaderParam("sortKey") - @DefaultValue("nom") + @DefaultValue("name") String sortKey; - @QueryParam("nom") - String nom; + @QueryParam("name") + String name; - @QueryParam("prenom") - String prenom; + @QueryParam("firstname") + String firstname; - @QueryParam("biographie") - String biographie; + @QueryParam("biography") + String biography; } diff --git a/src/main/java/fr/univtln/bruno/samples/jaxrs/server/BiblioServer.java b/src/main/java/fr/univtln/bruno/samples/jaxrs/server/BiblioServer.java index a4f1bd1f324a95d229a9838f64fb9827fd5bed97..bad83879d2d0025951d19dcac903d1f246bd3b56 100644 --- a/src/main/java/fr/univtln/bruno/samples/jaxrs/server/BiblioServer.java +++ b/src/main/java/fr/univtln/bruno/samples/jaxrs/server/BiblioServer.java @@ -22,7 +22,7 @@ public class BiblioServer { * The constant BASE_URI. */ // Base URI the Grizzly HTTP server will listen on - public static final String BASE_URI = "http://0.0.0.0:9998/myapp"; + public static final String BASE_URI = "http://0.0.0.0:9998/mylibrary"; public static final int TLS_PORT = 4443; diff --git a/src/test/java/fr/univtln/bruno/samples/jaxrs/BiblioModelAuteurTest.java b/src/test/java/fr/univtln/bruno/samples/jaxrs/LibraryModelAuthorTest.java similarity index 59% rename from src/test/java/fr/univtln/bruno/samples/jaxrs/BiblioModelAuteurTest.java rename to src/test/java/fr/univtln/bruno/samples/jaxrs/LibraryModelAuthorTest.java index 0f53b28a1cc87e68be6d43c1765893eca092bc02..d0014e5bb0e83a4433ceadc48af10e0279035279 100644 --- a/src/test/java/fr/univtln/bruno/samples/jaxrs/BiblioModelAuteurTest.java +++ b/src/test/java/fr/univtln/bruno/samples/jaxrs/LibraryModelAuthorTest.java @@ -1,24 +1,24 @@ package fr.univtln.bruno.samples.jaxrs; -import fr.univtln.bruno.samples.jaxrs.model.BiblioModel; +import fr.univtln.bruno.samples.jaxrs.model.Library; import org.junit.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; -public class BiblioModelAuteurTest { +public class LibraryModelAuthorTest { @Test public void AuteurCreationTest() { long id = 1; String nom = "Doe", prenom = "John", bio = "My life"; - BiblioModel.Auteur auteur = BiblioModel.Auteur.builder() + Library.Author author = Library.Author.builder() .id(id) - .nom(nom) - .prenom(prenom) - .biographie(bio) + .name(nom) + .firstname(prenom) + .biography(bio) .build(); - assertThat(auteur, allOf(hasProperty("nom", is(nom)), + assertThat(author, allOf(hasProperty("nom", is(nom)), hasProperty("prenom", is(prenom)), hasProperty("biographie", is(bio)))); } diff --git a/src/test/java/fr/univtln/bruno/samples/jaxrs/ServerIT.java b/src/test/java/fr/univtln/bruno/samples/jaxrs/ServerIT.java index aca5882296cd7449729e65014214faaa4c8492e8..640b77d0f2f21cf0fccfd0c5581b6f65068bd9f0 100644 --- a/src/test/java/fr/univtln/bruno/samples/jaxrs/ServerIT.java +++ b/src/test/java/fr/univtln/bruno/samples/jaxrs/ServerIT.java @@ -1,6 +1,7 @@ package fr.univtln.bruno.samples.jaxrs; -import fr.univtln.bruno.samples.jaxrs.model.BiblioModel.Auteur; +import fr.univtln.bruno.samples.jaxrs.model.Library; +import fr.univtln.bruno.samples.jaxrs.model.Library.Author; import fr.univtln.bruno.samples.jaxrs.security.InMemoryLoginModule; import fr.univtln.bruno.samples.jaxrs.server.BiblioServer; import io.jsonwebtoken.Claims; @@ -83,10 +84,10 @@ public class ServerIT { */ @Test public void testGetAuteurJSON() { - Auteur responseAuteur = webTarget.path("biblio/auteurs/1").request(MediaType.APPLICATION_JSON).get(Auteur.class); - assertNotNull(responseAuteur); - assertEquals("Alfred", responseAuteur.getPrenom()); - assertEquals("Martin", responseAuteur.getNom()); + Library.Author responseAuthor = webTarget.path("biblio/auteurs/1").request(MediaType.APPLICATION_JSON).get(Library.Author.class); + assertNotNull(responseAuthor); + assertEquals("Alfred", responseAuthor.getFirstname()); + assertEquals("Martin", responseAuthor.getName()); } /** @@ -94,10 +95,10 @@ public class ServerIT { */ @Test public void testGetAuteurXML() { - Auteur responseAuteur = webTarget.path("biblio/auteurs/1").request(MediaType.TEXT_XML).get(Auteur.class); - assertNotNull(responseAuteur); - assertEquals("Alfred", responseAuteur.getPrenom()); - assertEquals("Martin", responseAuteur.getNom()); + Library.Author responseAuthor = webTarget.path("biblio/auteurs/1").request(MediaType.TEXT_XML).get(Library.Author.class); + assertNotNull(responseAuthor); + assertEquals("Alfred", responseAuthor.getFirstname()); + assertEquals("Martin", responseAuthor.getName()); } /** @@ -114,9 +115,9 @@ public class ServerIT { */ @Test public void testGetAuteurs() { - Collection<Auteur> responseAuteurs = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(new GenericType<>() { + Collection<Library.Author> responseAuthors = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(new GenericType<>() { }); - assertEquals(2, responseAuteurs.size()); + assertEquals(2, responseAuthors.size()); } /** @@ -125,9 +126,9 @@ public class ServerIT { @Test public void deleteAuteurs() { webTarget.path("biblio/auteurs").request().delete(); - Collection<Auteur> responseAuteurs = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(new GenericType<>() { + Collection<Library.Author> responseAuthors = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(new GenericType<>() { }); - assertEquals(0, responseAuteurs.size()); + assertEquals(0, responseAuthors.size()); } /** @@ -136,10 +137,10 @@ public class ServerIT { @Test public void deleteAuteur() { webTarget.path("biblio/auteurs/1").request().delete(); - Collection<Auteur> responseAuteurs = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(new GenericType<>() { + Collection<Library.Author> responseAuthors = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(new GenericType<>() { }); - assertEquals(1, responseAuteurs.size()); - assertEquals(2, responseAuteurs.iterator().next().getId()); + assertEquals(1, responseAuthors.size()); + assertEquals(2, responseAuthors.iterator().next().getId()); } @@ -152,14 +153,14 @@ public class ServerIT { .request(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .post(Entity.entity("{\"nom\":\"Smith\",\"prenom\":\"John\",\"biographie\":\"My life\"}", MediaType.APPLICATION_JSON)); - Collection<Auteur> responseAuteurs = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(new GenericType<>() { + Collection<Author> responseAuthors = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(new GenericType<>() { }); - assertEquals(3, responseAuteurs.size()); - Auteur responseAuteur = webTarget.path("biblio/auteurs/3").request(MediaType.APPLICATION_JSON).get(Auteur.class); - assertNotNull(responseAuteur); - assertEquals("John", responseAuteur.getPrenom()); - assertEquals("Smith", responseAuteur.getNom()); - assertEquals("My life", responseAuteur.getBiographie()); + assertEquals(3, responseAuthors.size()); + Library.Author responseAuthor = webTarget.path("biblio/auteurs/3").request(MediaType.APPLICATION_JSON).get(Library.Author.class); + assertNotNull(responseAuthor); + assertEquals("John", responseAuthor.getFirstname()); + assertEquals("Smith", responseAuthor.getName()); + assertEquals("My life", responseAuthor.getBiography()); } /** @@ -171,11 +172,11 @@ public class ServerIT { .request(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .put(Entity.entity("{\"nom\":\"Doe\",\"prenom\":\"Jim\",\"biographie\":\"My weird life\"}", MediaType.APPLICATION_JSON)); - Auteur responseAuteur = webTarget.path("biblio/auteurs/1").request(MediaType.APPLICATION_JSON).get(Auteur.class); - assertNotNull(responseAuteur); - assertEquals("Jim", responseAuteur.getPrenom()); - assertEquals("Doe", responseAuteur.getNom()); - assertEquals("My weird life", responseAuteur.getBiographie()); + Author responseAuthor = webTarget.path("biblio/auteurs/1").request(MediaType.APPLICATION_JSON).get(Library.Author.class); + assertNotNull(responseAuthor); + assertEquals("Jim", responseAuthor.getFirstname()); + assertEquals("Doe", responseAuthor.getName()); + assertEquals("My weird life", responseAuthor.getBiography()); } /** @@ -207,13 +208,13 @@ public class ServerIT { */ @Test public void filter() { - List<Auteur> auteurs = webTarget.path("biblio/auteurs/filter") + List<Library.Author> authors = webTarget.path("biblio/auteurs/filter") .queryParam("prenom","Marie") .request(MediaType.APPLICATION_JSON) .get(new GenericType<>() {}); - assertEquals(1, auteurs.size()); - assertEquals("Marie", auteurs.get(0).getPrenom()); + assertEquals(1, authors.size()); + assertEquals("Marie", authors.get(0).getFirstname()); } @Test diff --git a/src/test/java/fr/univtln/bruno/samples/jaxrs/model/BiblioModelTest.java b/src/test/java/fr/univtln/bruno/samples/jaxrs/model/BiblioModelTest.java deleted file mode 100644 index bbe4ec8dab5663f9aef45d8165dbcf810cc6d6d1..0000000000000000000000000000000000000000 --- a/src/test/java/fr/univtln/bruno/samples/jaxrs/model/BiblioModelTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package fr.univtln.bruno.samples.jaxrs.model; - -import fr.univtln.bruno.samples.jaxrs.exceptions.IllegalArgumentException; -import fr.univtln.bruno.samples.jaxrs.exceptions.NotFoundException; -import org.apache.commons.lang3.SerializationUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.samePropertyValuesAs; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class BiblioModelTest { - private static final BiblioModel modeleBibliotheque = BiblioModel.of(); - private static final BiblioModel.Auteur auteur1 = BiblioModel.Auteur.builder().prenom("Jean").nom("Martin").build(); - private static final BiblioModel.Auteur auteur2 = BiblioModel.Auteur.builder().prenom("Marie").nom("Durand").build(); - - /** - * Adds two authors before each tests. - */ - @Before - public void beforeEach() throws IllegalArgumentException { - modeleBibliotheque.addAuteur(SerializationUtils.clone(auteur1)); - modeleBibliotheque.addAuteur(SerializationUtils.clone(auteur2)); - } - - @After - public void afterEach() { - modeleBibliotheque.supprimerAuteurs(); - } - - @Test - public void addAuteur() throws IllegalArgumentException, NotFoundException { - BiblioModel.Auteur auteur = BiblioModel.Auteur.builder().prenom("John").nom("Doe").biographie("My life").build(); - modeleBibliotheque.addAuteur(SerializationUtils.clone(auteur)); - assertThat(auteur, samePropertyValuesAs(modeleBibliotheque.getAuteur(3), "id")); - } - - @Test(expected = NotFoundException.class) - public void addAuteurException() throws IllegalArgumentException, NotFoundException { - BiblioModel.Auteur auteur = BiblioModel.Auteur.builder().prenom("John").nom("Doe").build(); - modeleBibliotheque.addAuteur(SerializationUtils.clone(auteur)); - assertThat(auteur, samePropertyValuesAs(modeleBibliotheque.getAuteur(4), "id")); - } - - @Test - public void updateAuteur() throws IllegalArgumentException, NotFoundException { - BiblioModel.Auteur auteur = BiblioModel.Auteur.builder().prenom("John").nom("Doe").build(); - modeleBibliotheque.updateAuteur(1, SerializationUtils.clone(auteur)); - assertThat(auteur, samePropertyValuesAs(modeleBibliotheque.getAuteur(1), "id")); - } - - @Test - public void removeAuteur() throws NotFoundException { - modeleBibliotheque.removeAuteur(1); - assertEquals(1, modeleBibliotheque.getAuteurSize()); - assertEquals(2, modeleBibliotheque.getAuteurs().values().iterator().next().getId()); - } - - @Test - public void getAuteur() throws NotFoundException { - BiblioModel.Auteur auteur = modeleBibliotheque.getAuteur(1); - assertThat(auteur, samePropertyValuesAs(auteur1,"id")); - } - - @Test - public void getAuteurSize() { - assertEquals(2, modeleBibliotheque.getAuteurSize()); - } - - @Test - public void supprimerAuteurs() { - modeleBibliotheque.supprimerAuteurs(); - assertEquals(0,modeleBibliotheque.getAuteurSize()); - assertEquals(0, modeleBibliotheque.getAuteurs().size()); - } - - @Test - public void of() { - BiblioModel modeleBibliotheque1 = BiblioModel.of(); - assertNotNull(modeleBibliotheque1); - } - - @Test - public void getAuteurs() { - assertNotNull(modeleBibliotheque.getAuteurs()); - } -} \ No newline at end of file diff --git a/src/test/java/fr/univtln/bruno/samples/jaxrs/model/LibraryModelTest.java b/src/test/java/fr/univtln/bruno/samples/jaxrs/model/LibraryModelTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6a93869fc7648b994289d7fd93e1c7d8aede6ee8 --- /dev/null +++ b/src/test/java/fr/univtln/bruno/samples/jaxrs/model/LibraryModelTest.java @@ -0,0 +1,90 @@ +package fr.univtln.bruno.samples.jaxrs.model; + +import fr.univtln.bruno.samples.jaxrs.exceptions.BusinessException; +import fr.univtln.bruno.samples.jaxrs.exceptions.NotFoundException; +import org.apache.commons.lang3.SerializationUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.samePropertyValuesAs; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class LibraryModelTest { + private static final Library modeleBibliotheque = Library.newInstance(); + private static final Library.Author AUTHOR_1 = Library.Author.builder().firstname("Jean").name("Martin").build(); + private static final Library.Author AUTHOR_2 = Library.Author.builder().firstname("Marie").name("Durand").build(); + + /** + * Adds two authors before each tests. + */ + @Before + public void beforeEach() throws BusinessException { + modeleBibliotheque.addAuthor(SerializationUtils.clone(AUTHOR_1)); + modeleBibliotheque.addAuthor(SerializationUtils.clone(AUTHOR_2)); + } + + @After + public void afterEach() { + modeleBibliotheque.removesAuthors(); + } + + @Test + public void addAuteur() throws BusinessException { + Library.Author author = Library.Author.builder().firstname("John").name("Doe").biography("My life").build(); + modeleBibliotheque.addAuthor(SerializationUtils.clone(author)); + assertThat(author, samePropertyValuesAs(modeleBibliotheque.getAuthor(3), "id")); + } + + @Test(expected = NotFoundException.class) + public void addAuteurException() throws BusinessException { + Library.Author author = Library.Author.builder().firstname("John").name("Doe").build(); + modeleBibliotheque.addAuthor(SerializationUtils.clone(author)); + assertThat(author, samePropertyValuesAs(modeleBibliotheque.getAuthor(4), "id")); + } + + @Test + public void updateAuteur() throws BusinessException { + Library.Author author = Library.Author.builder().firstname("John").name("Doe").build(); + modeleBibliotheque.updateAuteur(1, SerializationUtils.clone(author)); + assertThat(author, samePropertyValuesAs(modeleBibliotheque.getAuthor(1), "id")); + } + + @Test + public void removeAuteur() throws BusinessException { + modeleBibliotheque.removeAuthor(1); + assertEquals(1, modeleBibliotheque.getAuthorsNumber()); + assertEquals(2, modeleBibliotheque.getAuthors().values().iterator().next().getId()); + } + + @Test + public void getAuteur() throws BusinessException { + Library.Author author = modeleBibliotheque.getAuthor(1); + assertThat(author, samePropertyValuesAs(AUTHOR_1,"id")); + } + + @Test + public void getAuteurSize() { + assertEquals(2, modeleBibliotheque.getAuthorsNumber()); + } + + @Test + public void supprimerAuteurs() { + modeleBibliotheque.removesAuthors(); + assertEquals(0,modeleBibliotheque.getAuthorsNumber()); + assertEquals(0, modeleBibliotheque.getAuthors().size()); + } + + @Test + public void of() { + Library modeleBibliotheque1 = Library.newInstance(); + assertNotNull(modeleBibliotheque1); + } + + @Test + public void getAuteurs() { + assertNotNull(modeleBibliotheque.getAuthors()); + } +} \ No newline at end of file