diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..45afd4b
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,15 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
+
+version: 2
+updates:
+ - package-ecosystem: "maven" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ - package-ecosystem: github-actions
+ directory: /
+ schedule:
+ interval: daily
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
new file mode 100644
index 0000000..5d27e88
--- /dev/null
+++ b/.github/workflows/maven.yml
@@ -0,0 +1,26 @@
+name: Java CI with Maven
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v6
+ - name: Set up JDK 21
+ uses: actions/setup-java@v5
+ with:
+ java-version: '21'
+ distribution: 'temurin'
+ cache: maven
+ - name: Build with Maven
+ run: mvn -B compile
diff --git a/README.md b/README.md
index f831728..60f99a2 100644
--- a/README.md
+++ b/README.md
@@ -2,13 +2,14 @@
## Goal
- - Demonstrate how to generate arbitrary documentation from your application
- - Additionally, show how to use [Hibernate Data Repositories](https://docs.hibernate.org/orm/7.1/repositories/html_single/)
+- Demonstrate how to generate arbitrary documentation from your application
+- Additionally, show how to
+ use [Hibernate Data Repositories](https://docs.hibernate.org/orm/7.1/repositories/html_single/)
## Requirements
- - Java 21
- - Docker for booting mySQL
+- Java 21
+- Docker for booting mySQL
## Run
@@ -16,15 +17,15 @@
## Output:
- - http://localhost:8080/docs
- - http://localhost:8080/docs/full.html
- - http://localhost:8080/docs/tryIt.html
+- http://localhost:8080/docs
+- http://localhost:8080/docs/full.html
+- http://localhost:8080/docs/tryIt.html
- - http://localhost:8080/redoc
- - http://localhost:8080/swagger
+- http://localhost:8080/redoc
+- http://localhost:8080/swagger
## Documentation and usage
- - https://jooby.io/modules/openapi
- - https://jooby.io/modules/openapi/#openapi-outputdisplay
+- https://jooby.io/modules/openapi
+- https://jooby.io/modules/openapi/#openapi-outputdisplay
diff --git a/conf/logback.xml b/conf/logback.xml
index bdb1e0b..2ccd976 100644
--- a/conf/logback.xml
+++ b/conf/logback.xml
@@ -1,12 +1,12 @@
-
-
- [%d{ISO8601}]-[%thread] %-5level %logger - %msg%n
-
-
+
+
+ [%d{ISO8601}]-[%thread] %-5level %logger - %msg%n
+
+
-
-
-
+
+
+
diff --git a/pom.xml b/pom.xml
index 64b50c4..6547d0a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,188 +1,190 @@
-
- 4.0.0
+ 4.0.0
- library-demo
- app
- 1.0.0
+ library-demo
+ app
+ 1.0.0
- library-demo
+ library-demo
-
-
- app.App
- ${project.basedir}${file.separator}src${file.separator}main${file.separator}resources${file.separator}docs${file.separator}
+
+
+ app.App
+
+ ${project.basedir}${file.separator}src${file.separator}main${file.separator}resources${file.separator}docs${file.separator}
+
- 4.0.13
- java,adoc
+ 4.0.15
+ java,adoc
- 21
- 21
- true
- UTF-8
-
+ 21
+ 21
+ true
+ UTF-8
+
-
-
- io.jooby
- jooby-logback
-
-
- io.jooby
- jooby-netty
-
-
- io.jooby
- jooby-guice
-
-
- io.jooby
- jooby-jackson
-
-
- io.jooby
- jooby-hikari
-
-
- io.jooby
- jooby-flyway
-
-
- org.flywaydb
- flyway-mysql
- 11.14.0
-
-
- io.jooby
- jooby-hibernate
-
-
- jakarta.data
- jakarta.data-api
- 1.0.1
-
-
- com.mysql
- mysql-connector-j
- 9.4.0
-
-
-
- io.jooby
- jooby-redoc
-
-
- io.jooby
- jooby-swagger-ui
-
-
-
- org.junit.jupiter
- junit-jupiter-api
- 5.13.4
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- 5.13.4
- test
-
-
-
-
-
-
- conf
-
-
- src${file.separator}main${file.separator}resources
-
-
-
-
- maven-compiler-plugin
- 3.14.0
-
-
-
- org.hibernate.orm
- hibernate-processor
- 7.0.4.Final
-
-
- io.jooby
- jooby-apt
- ${jooby.version}
-
-
-
-
-
- maven-surefire-plugin
- 3.5.3
-
-
-
- io.jooby
- jooby-maven-plugin
- ${jooby.version}
-
-
-
- openapi
-
-
- 3.1
-
- ${doc.dir}index.adoc
- ${doc.dir}full.adoc
- ${doc.dir}tryIt.adoc
-
-
-
-
-
-
-
- maven-shade-plugin
- 3.6.0
-
-
- uber-jar
- package
-
- shade
-
-
- false
-
-
-
- ${application.class}
-
-
-
-
-
-
-
-
-
-
-
- io.jooby
- jooby-bom
- ${jooby.version}
- pom
- import
-
+
+ io.jooby
+ jooby-logback
+
+
+ io.jooby
+ jooby-netty
+
+
+ io.jooby
+ jooby-guice
+
+
+ io.jooby
+ jooby-jackson
+
+
+ io.jooby
+ jooby-hikari
+
+
+ io.jooby
+ jooby-flyway
+
+
+ org.flywaydb
+ flyway-mysql
+ 12.0.0
+
+
+ io.jooby
+ jooby-hibernate
+
+
+ jakarta.data
+ jakarta.data-api
+ 1.0.1
+
+
+ com.mysql
+ mysql-connector-j
+ 9.6.0
+
+
+
+ io.jooby
+ jooby-redoc
+
+
+ io.jooby
+ jooby-swagger-ui
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 6.0.2
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 6.0.2
+ test
+
-
+
+
+
+
+ conf
+
+
+ src${file.separator}main${file.separator}resources
+
+
+
+
+ maven-compiler-plugin
+ 3.15.0
+
+
+
+ org.hibernate.orm
+ hibernate-processor
+ 7.2.3.Final
+
+
+ io.jooby
+ jooby-apt
+ ${jooby.version}
+
+
+
+
+
+ maven-surefire-plugin
+ 3.5.4
+
+
+
+ io.jooby
+ jooby-maven-plugin
+ ${jooby.version}
+
+
+
+ openapi
+
+
+ 3.1
+
+ ${doc.dir}index.adoc
+ ${doc.dir}full.adoc
+ ${doc.dir}tryIt.adoc
+
+
+
+
+
+
+
+ maven-shade-plugin
+ 3.6.1
+
+
+ uber-jar
+ package
+
+ shade
+
+
+ false
+
+
+
+ ${application.class}
+
+
+
+
+
+
+
+
+
+
+
+
+ io.jooby
+ jooby-bom
+ ${jooby.version}
+ pom
+ import
+
+
+
diff --git a/src/main/java/app/App.java b/src/main/java/app/App.java
index 6dbcd95..17dd5b5 100644
--- a/src/main/java/app/App.java
+++ b/src/main/java/app/App.java
@@ -5,7 +5,10 @@
import app.model.Author;
import app.model.Book;
import app.model.Publisher;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import io.jooby.Jooby;
+import io.jooby.OpenAPIModule;
import io.jooby.flyway.FlywayModule;
import io.jooby.guice.GuiceModule;
import io.jooby.hibernate.HibernateModule;
@@ -13,16 +16,17 @@
import io.jooby.hikari.HikariModule;
import io.jooby.jackson.JacksonModule;
import io.jooby.netty.NettyServer;
-import io.jooby.OpenAPIModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.concurrent.Executors;
+
/**
* Library API.
*
* An imaginary, but delightful Library API for interacting with library services and information.
* Built with love by https://jooby.io.
- *
+ *
*
* @version 1.0.0
* @license.name Apache 2.0
@@ -44,36 +48,43 @@
*/
public class App extends Jooby {
- private static final Logger log = LoggerFactory.getLogger(App.class);
+ private static final Logger log = LoggerFactory.getLogger(App.class);
+
+ {
+ // Enable Virtual Threads
+ setDefaultWorker(Executors.newVirtualThreadPerTaskExecutor());
+
+ // Dependency Injection
+ install(new GuiceModule());
+
+ // JSON
+ ObjectMapper objectMapper = JacksonModule.create().
+ setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
- {
- // Dependency Injection
- install(new GuiceModule());
- // JSON
- install(new JacksonModule());
+ install(new JacksonModule(objectMapper));
- /* Documentation */
- install(new OpenAPIModule());
- // Publish generated files
- assets("/docs/?*", "/app");
+ /* Documentation */
+ install(new OpenAPIModule());
+ // Publish generated files
+ assets("/docs/?*", "/app");
- /* Database */
- install(new HikariModule());
- install(new FlywayModule());
- install(new HibernateModule(Book.class, Author.class, Publisher.class, Address.class));
+ /* Database */
+ install(new HikariModule());
+ install(new FlywayModule());
+ install(new HibernateModule(Book.class, Author.class, Publisher.class, Address.class));
- /* Middleware */
- use(new TransactionalRequest().useStatelessSession());
+ /* Middleware */
+ use(new TransactionalRequest().useStatelessSession());
- /* Controller */
- mvc(new LibraryApi_());
+ /* Controller */
+ mvc(new LibraryApi_());
- onStarted(() -> {
- log.info("\n\nTry live docs at: \n http://localhost:8080/docs\n http://localhost:8080/docs/full.html\n http://localhost:8080/docs/tryIt.html\n\n http://localhost:8080/redoc\n http://localhost:8080/swagger\n");
- });
- }
+ onStarted(() -> {
+ log.info("\n\nTry live docs at: \n http://localhost:8080/docs\n http://localhost:8080/docs/full.html\n http://localhost:8080/docs/tryIt.html\n\n http://localhost:8080/redoc\n http://localhost:8080/swagger\n");
+ });
+ }
- public static void main(final String[] args) {
- runApp(args, new NettyServer(), App::new);
- }
+ public static void main(final String[] args) {
+ runApp(args, new NettyServer(), App::new);
+ }
}
diff --git a/src/main/java/app/api/LibraryApi.java b/src/main/java/app/api/LibraryApi.java
index 71cc447..4537faa 100644
--- a/src/main/java/app/api/LibraryApi.java
+++ b/src/main/java/app/api/LibraryApi.java
@@ -17,90 +17,87 @@
@Path("/library")
public class LibraryApi {
- private final Library library;
+ private final Library library;
- @Inject
- public LibraryApi(Library library) {
- this.library = library;
- }
+ @Inject
+ public LibraryApi(Library library) {
+ this.library = library;
+ }
- /**
- * Get Specific Book Details
- *
- * View the full information for a single specific book using its unique ISBN.
- *
- *
- * @param isbn The unique ID from the URL (e.g., /books/978-3-16-148410-0)
- * @return The book data
- * @throws NotFoundException 404 error if it doesn't exist.
- * @tag Library
- * @securityRequirement librarySecurity read:books
- */
- @GET
- @Path("/books/{isbn}")
- public Book getBook(@PathParam String isbn) {
- return library.findBook(isbn)
- .orElseThrow(() -> new NotFoundException(isbn));
- }
+ /**
+ * Get Specific Book Details
+ *
+ * View the full information for a single specific book using its unique ISBN.
+ *
+ *
+ * @param isbn The unique ID from the URL (e.g., /books/978-3-16-148410-0)
+ * @return The book data
+ * @throws NotFoundException 404 error if it doesn't exist.
+ * @tag Library
+ * @securityRequirement librarySecurity read:books
+ */
+ @GET
+ @Path("/books/{isbn}")
+ public Book getBook(@PathParam String isbn) {
+ return library.findBook(isbn).orElseThrow(() -> new NotFoundException(isbn));
+ }
- /**
- * Quick Search
- * Find books by a partial title (e.g., searching "Harry" finds "Harry Potter").
- *
- * @param q The word or phrase to search for.
- * @return A list of books matching that term.
- * @x-badges [{name:Beta, position:before, color:purple}]
- * @tag Library
- * @securityRequirement librarySecurity read:books
- */
- @GET
- @Path("/search")
- public List searchBooks(@QueryParam String q) {
- var pattern = "%" + (q != null ? q : "") + "%";
+ /**
+ * Quick Search
+ * Find books by a partial title (e.g., searching "Harry" finds "Harry Potter").
+ *
+ * @param q The word or phrase to search for.
+ * @return A list of books matching that term.
+ * @x-badges [{name:Beta, position:before, color:purple}]
+ * @tag Library
+ * @securityRequirement librarySecurity read:books
+ */
+ @GET
+ @Path("/search")
+ public List searchBooks(@QueryParam String q) {
+ var pattern = "%" + (q != null ? q : "") + "%";
- return library.searchBooks(pattern);
- }
+ return library.searchBooks(pattern);
+ }
- /**
- * Browse Books (Paginated)
- * Look up a specific book title where there might be many editions or copies, splitting the results into
- * manageable pages.
- *
- * @param title The exact book title to filter by.
- * @param page Which page number to load (defaults to 1).
- * @param size How many books to show per page (defaults to 20).
- * @return A "Page" object containing the books and info like "Total Pages: 5".
- * @tag Library
- * @securityRequirement librarySecurity read:books
- */
- @GET
- @Path("/books")
- public Page getBooksByTitle(@QueryParam String title,
- @QueryParam int page,
- @QueryParam int size) {
- // Ensure we have sensible defaults if the user sends nothing
- int pageNum = page > 0 ? page : 1;
- int pageSize = size > 0 ? size : 20;
+ /**
+ * Browse Books (Paginated)
+ * Look up a specific book title where there might be many editions or copies, splitting the results into
+ * manageable pages.
+ *
+ * @param title The exact book title to filter by.
+ * @param page Which page number to load (defaults to 1).
+ * @param size How many books to show per page (defaults to 20).
+ * @return A "Page" object containing the books and info like "Total Pages: 5".
+ * @tag Library
+ * @securityRequirement librarySecurity read:books
+ */
+ @GET
+ @Path("/books")
+ public Page getBooksByTitle(@QueryParam String title, @QueryParam int page, @QueryParam int size) {
+ // Ensure we have sensible defaults if the user sends nothing
+ int pageNum = page > 0 ? page : 1;
+ int pageSize = size > 0 ? size : 20;
- // Ask the database for just this specific slice of data
- return library.findBooksByTitle(title, PageRequest.ofPage(pageNum).size(pageSize));
- }
+ // Ask the database for just this specific slice of data
+ return library.findBooksByTitle(title, PageRequest.ofPage(pageNum).size(pageSize));
+ }
- /**
- * Add New Book
- *
- * Register a new book in the system.
- *
- * @param book New book to add.
- * @return A text message confirming success.
- * @throws io.jooby.exception.BadRequestException 400 On bad book data.
- * @tag Inventory
- * @securityRequirement librarySecurity write:books
- */
- @POST
- @Path("/books")
- public Book addBook(Book book) {
- // Save it
- return library.add(book);
- }
+ /**
+ * Add New Book
+ *
+ *
Register a new book in the system.
+ *
+ * @param book New book to add.
+ * @return A text message confirming success.
+ * @throws io.jooby.exception.BadRequestException 400 On bad book data.
+ * @tag Inventory
+ * @securityRequirement librarySecurity write:books
+ */
+ @POST
+ @Path("/books")
+ public Book addBook(Book book) {
+ // Save it
+ return library.add(book);
+ }
}
diff --git a/src/main/java/app/model/Address.java b/src/main/java/app/model/Address.java
index 4c3b2d3..57db7f8 100644
--- a/src/main/java/app/model/Address.java
+++ b/src/main/java/app/model/Address.java
@@ -8,29 +8,29 @@
*/
@Embeddable
public class Address {
- /**
- * The specific street address.
- *
- * Includes the house number, street name, and apartment number if applicable.
- * Example: "123 Maple Avenue, Apt 4B".
- *
- */
- public String street;
+ /**
+ * The specific street address.
+ *
+ * Includes the house number, street name, and apartment number if applicable.
+ * Example: "123 Maple Avenue, Apt 4B".
+ *
+ */
+ public String street;
- /**
- * The town, city, or municipality.
- *
- * Used for grouping authors by location or calculating shipping regions.
- *
- */
- public String city;
+ /**
+ * The town, city, or municipality.
+ *
+ * Used for grouping authors by location or calculating shipping regions.
+ *
+ */
+ public String city;
- /**
- * The postal or zip code.
- *
- * Stored as text (String) rather than a number to support codes
- * that start with zero (e.g., "02138") or contain letters (e.g., "K1A 0B1").
- *
- */
- public String zip;
+ /**
+ * The postal or zip code.
+ *
+ * Stored as text (String) rather than a number to support codes
+ * that start with zero (e.g., "02138") or contain letters (e.g., "K1A 0B1").
+ *
+ */
+ public String zip;
}
diff --git a/src/main/java/app/model/Author.java b/src/main/java/app/model/Author.java
index 386ce0e..f5958db 100644
--- a/src/main/java/app/model/Author.java
+++ b/src/main/java/app/model/Author.java
@@ -15,33 +15,34 @@
@Entity
public class Author {
- /**
- * The author's unique government ID (SSN).
- */
- @Id
- public String ssn;
-
- /**
- * The full name of the author.
- */
- public String name;
-
- /**
- * Where the author lives.
- * This information is stored inside the Author table, not a separate one.
- */
- @Embedded
- public Address address;
-
- @ManyToMany
- @JsonIgnore
- public Set books = new HashSet<>();
-
- public Author() {}
-
- public Author(String ssn, String name) {
- this.ssn = ssn;
- this.name = name;
- }
+ /**
+ * The author's unique government ID (SSN).
+ */
+ @Id
+ public String ssn;
+
+ /**
+ * The full name of the author.
+ */
+ public String name;
+
+ /**
+ * Where the author lives.
+ * This information is stored inside the Author table, not a separate one.
+ */
+ @Embedded
+ public Address address;
+
+ @ManyToMany
+ @JsonIgnore
+ public Set books = new HashSet<>();
+
+ public Author() {
+ }
+
+ public Author(String ssn, String name) {
+ this.ssn = ssn;
+ this.name = name;
+ }
}
diff --git a/src/main/java/app/model/Book.java b/src/main/java/app/model/Book.java
index 09dd579..430e6e2 100644
--- a/src/main/java/app/model/Book.java
+++ b/src/main/java/app/model/Book.java
@@ -16,121 +16,121 @@
@Entity
public class Book {
- /**
- * The unique "barcode" for this book (ISBN).
- * We use this to identify exactly which book edition we are talking about.
- */
- @Id
- private String isbn;
-
- /**
- * The name printed on the cover.
- */
- @Basic(optional = false)
- private String title;
-
- /**
- * When this book was released to the public.
- */
- private LocalDate publicationDate;
-
- /**
- * The full story or content of the book.
- *
- * Since this can be very long, we store it in a special way (Large Object)
- * to keep the database fast.
- *
- */
- @Lob
- @Basic(optional = false)
- private String text;
-
- /**
- * Categorizes the item (e.g., is it a regular Book or a Magazine?).
- */
- @Enumerated(EnumType.STRING)
- @Basic(optional = false)
- private BookType type;
-
- /**
- * The company that published this book.
- *
- * Performance Note: We only load this information if you specifically
- * ask for it ("Lazy"), which saves memory.
- *
- */
- @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
- private Publisher publisher;
-
- /**
- * The list of people who wrote this book.
- */
- @ManyToMany(fetch = FetchType.EAGER, mappedBy = "books", cascade = CascadeType.PERSIST)
- private Set authors = new HashSet<>();
-
- public Book() {
- }
-
- public Book(String isbn, String title, BookType type) {
- this.isbn = isbn;
- this.title = title;
- this.type = type;
- this.text = "Content placeholder";
- }
-
- public String getIsbn() {
- return isbn;
- }
-
- public void setIsbn(String isbn) {
- this.isbn = isbn;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public LocalDate getPublicationDate() {
- return publicationDate;
- }
-
- public void setPublicationDate(LocalDate publicationDate) {
- this.publicationDate = publicationDate;
- }
-
- public String getText() {
- return text;
- }
-
- public void setText(String text) {
- this.text = text;
- }
-
- public BookType getType() {
- return type;
- }
-
- public void setType(BookType type) {
- this.type = type;
- }
-
- public Publisher getPublisher() {
- return publisher;
- }
-
- public void setPublisher(Publisher publisher) {
- this.publisher = publisher;
- }
-
- public Set getAuthors() {
- return authors;
- }
-
- public void setAuthors(Set authors) {
- this.authors = authors;
- }
+ /**
+ * The unique "barcode" for this book (ISBN).
+ * We use this to identify exactly which book edition we are talking about.
+ */
+ @Id
+ private String isbn;
+
+ /**
+ * The name printed on the cover.
+ */
+ @Basic(optional = false)
+ private String title;
+
+ /**
+ * When this book was released to the public.
+ */
+ private LocalDate publicationDate;
+
+ /**
+ * The full story or content of the book.
+ *
+ * Since this can be very long, we store it in a special way (Large Object)
+ * to keep the database fast.
+ *
+ */
+ @Lob
+ @Basic(optional = false)
+ private String text;
+
+ /**
+ * Categorizes the item (e.g., is it a regular Book or a Magazine?).
+ */
+ @Enumerated(EnumType.STRING)
+ @Basic(optional = false)
+ private BookType type;
+
+ /**
+ * The company that published this book.
+ *
+ * Performance Note: We only load this information if you specifically
+ * ask for it ("Lazy"), which saves memory.
+ *
+ */
+ @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
+ private Publisher publisher;
+
+ /**
+ * The list of people who wrote this book.
+ */
+ @ManyToMany(fetch = FetchType.EAGER, mappedBy = "books", cascade = CascadeType.PERSIST)
+ private Set authors = new HashSet<>();
+
+ public Book() {
+ }
+
+ public Book(String isbn, String title, BookType type) {
+ this.isbn = isbn;
+ this.title = title;
+ this.type = type;
+ this.text = "Content placeholder";
+ }
+
+ public String getIsbn() {
+ return isbn;
+ }
+
+ public void setIsbn(String isbn) {
+ this.isbn = isbn;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public LocalDate getPublicationDate() {
+ return publicationDate;
+ }
+
+ public void setPublicationDate(LocalDate publicationDate) {
+ this.publicationDate = publicationDate;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public BookType getType() {
+ return type;
+ }
+
+ public void setType(BookType type) {
+ this.type = type;
+ }
+
+ public Publisher getPublisher() {
+ return publisher;
+ }
+
+ public void setPublisher(Publisher publisher) {
+ this.publisher = publisher;
+ }
+
+ public Set getAuthors() {
+ return authors;
+ }
+
+ public void setAuthors(Set authors) {
+ this.authors = authors;
+ }
}
diff --git a/src/main/java/app/model/BookType.java b/src/main/java/app/model/BookType.java
index 5199508..89e20ca 100644
--- a/src/main/java/app/model/BookType.java
+++ b/src/main/java/app/model/BookType.java
@@ -4,51 +4,51 @@
* Defines the format and release schedule of the item.
*/
public enum BookType {
- /**
- * A fictional narrative story.
- *
- * Examples: "Pride and Prejudice", "Harry Potter", "Dune".
- * These are creative works meant for entertainment or artistic expression.
- *
- */
- NOVEL,
+ /**
+ * A fictional narrative story.
+ *
+ * Examples: "Pride and Prejudice", "Harry Potter", "Dune".
+ * These are creative works meant for entertainment or artistic expression.
+ *
+ */
+ NOVEL,
- /**
- * A written account of a real person's life.
- *
- * Examples: "Steve Jobs" by Walter Isaacson, "The Diary of a Young Girl".
- * These are non-fiction historical records of an individual.
- *
- */
- BIOGRAPHY,
+ /**
+ * A written account of a real person's life.
+ *
+ * Examples: "Steve Jobs" by Walter Isaacson, "The Diary of a Young Girl".
+ * These are non-fiction historical records of an individual.
+ *
+ */
+ BIOGRAPHY,
- /**
- * An educational book used for study.
- *
- * Examples: "Calculus: Early Transcendentals", "Introduction to Java Programming".
- * These are designed for students and are often used as reference material
- * in academic courses.
- *
- */
- TEXTBOOK,
+ /**
+ * An educational book used for study.
+ *
+ * Examples: "Calculus: Early Transcendentals", "Introduction to Java Programming".
+ * These are designed for students and are often used as reference material
+ * in academic courses.
+ *
+ */
+ TEXTBOOK,
- /**
- * A periodical publication intended for general readers.
- *
- * Examples: Time, National Geographic, Vogue.
- * These contain various articles, are published frequently (weekly/monthly),
- * and often have a glossy format.
- *
- */
- MAGAZINE,
+ /**
+ * A periodical publication intended for general readers.
+ *
+ * Examples: Time, National Geographic, Vogue.
+ * These contain various articles, are published frequently (weekly/monthly),
+ * and often have a glossy format.
+ *
+ */
+ MAGAZINE,
- /**
- * A scholarly or professional publication.
- *
- * Examples: The New England Journal of Medicine, Harvard Law Review.
- * These focus on academic research or trade news and are written by experts
- * for other experts.
- *
- */
- JOURNAL
+ /**
+ * A scholarly or professional publication.
+ *
+ * Examples: The New England Journal of Medicine, Harvard Law Review.
+ * These focus on academic research or trade news and are written by experts
+ * for other experts.
+ *
+ */
+ JOURNAL
}
diff --git a/src/main/java/app/model/Publisher.java b/src/main/java/app/model/Publisher.java
index 2cc8e01..b08ba43 100644
--- a/src/main/java/app/model/Publisher.java
+++ b/src/main/java/app/model/Publisher.java
@@ -9,26 +9,30 @@
*/
@Entity
public class Publisher {
- /**
- * The unique internal ID for this publisher.
- *
- * This is a number generated automatically by the system.
- * Users usually don't need to memorize this, but it's used by the database
- * to link books to their publishers.
- *
- */
- @Id
- @GeneratedValue
- public Long id;
+ /**
+ * The unique internal ID for this publisher.
+ *
+ * This is a number generated automatically by the system.
+ * Users usually don't need to memorize this, but it's used by the database
+ * to link books to their publishers.
+ *
+ */
+ @Id
+ @GeneratedValue
+ public Long id;
- /**
- * The official business name of the publishing house.
- *
- * Example: "Penguin Random House" or "O'Reilly Media".
- *
- */
- public String name;
+ /**
+ * The official business name of the publishing house.
+ *
+ * Example: "Penguin Random House" or "O'Reilly Media".
+ *
+ */
+ public String name;
- public Publisher() {}
- public Publisher(String name) { this.name = name; }
+ public Publisher() {
+ }
+
+ public Publisher(String name) {
+ this.name = name;
+ }
}
diff --git a/src/main/java/app/repo/Library.java b/src/main/java/app/repo/Library.java
index c08cd93..99321f6 100644
--- a/src/main/java/app/repo/Library.java
+++ b/src/main/java/app/repo/Library.java
@@ -23,83 +23,83 @@
@ImplementedBy(Library_.class)
public interface Library {
- /**
- * A helper to access the database connection directly.
- * Useful if we need to do something very specific that the automatic methods can't handle.
- */
- StatelessSession session();
+ /**
+ * A helper to access the database connection directly.
+ * Useful if we need to do something very specific that the automatic methods can't handle.
+ */
+ StatelessSession session();
- // --- Finding Items ---
+ // --- Finding Items ---
- /**
- * Looks up a single book using its ISBN code.
- *
- * @param isbn The unique code to look for.
- * @return An "Optional" box that contains the book if we found it, or is empty if we didn't.
- */
- @Find
- Optional findBook(String isbn);
+ /**
+ * Looks up a single book using its ISBN code.
+ *
+ * @param isbn The unique code to look for.
+ * @return An "Optional" box that contains the book if we found it, or is empty if we didn't.
+ */
+ @Find
+ Optional findBook(String isbn);
- /**
- * Looks up an author using their ID.
- */
- @Find
- Optional findAuthor(String ssn);
+ /**
+ * Looks up an author using their ID.
+ */
+ @Find
+ Optional findAuthor(String ssn);
- /**
- * Finds books that match a specific title.
- *
- * Because there might be thousands of results, this method splits them into "pages".
- * You ask for "Page 1" or "Page 5", and it gives you just that chunk.
- *
- *
- * @param title The exact title to look for.
- * @param pageRequest Which page of results do you want?
- * @return A page containing a list of books and total count info.
- */
- @Find
- @OrderBy("title")
- Page findBooksByTitle(String title, PageRequest pageRequest);
+ /**
+ * Finds books that match a specific title.
+ *
+ * Because there might be thousands of results, this method splits them into "pages".
+ * You ask for "Page 1" or "Page 5", and it gives you just that chunk.
+ *
+ *
+ * @param title The exact title to look for.
+ * @param pageRequest Which page of results do you want?
+ * @return A page containing a list of books and total count info.
+ */
+ @Find
+ @OrderBy("title")
+ Page findBooksByTitle(String title, PageRequest pageRequest);
- // --- Custom Searches ---
+ // --- Custom Searches ---
- /**
- * Search for books that have a specific word in the title.
- *
- * Example: If you search for "%Harry%", it finds "Harry Potter" and "Dirty Harry".
- * It also sorts the results alphabetically by title.
- *
- */
- @Query("where title like :pattern order by title")
- List searchBooks(String pattern);
+ /**
+ * Search for books that have a specific word in the title.
+ *
+ * Example: If you search for "%Harry%", it finds "Harry Potter" and "Dirty Harry".
+ * It also sorts the results alphabetically by title.
+ *
+ */
+ @Query("where title like :pattern order by title")
+ List searchBooks(String pattern);
- /**
- * A custom report that just lists the titles of new books.
- * Useful for creating quick lists without loading all the book details.
- *
- * @param minYear The oldest year we care about (e.g., 2023).
- * @return Just the names of the books.
- */
- @Query("select title from Book where extract(year from publicationDate) >= :minYear")
- List findRecentBookTitles(int minYear);
+ /**
+ * A custom report that just lists the titles of new books.
+ * Useful for creating quick lists without loading all the book details.
+ *
+ * @param minYear The oldest year we care about (e.g., 2023).
+ * @return Just the names of the books.
+ */
+ @Query("select title from Book where extract(year from publicationDate) >= :minYear")
+ List findRecentBookTitles(int minYear);
- // --- Saving & Deleting ---
+ // --- Saving & Deleting ---
- /**
- * Registers a new book in the system.
- */
- @Insert
- Book add(Book book);
+ /**
+ * Registers a new book in the system.
+ */
+ @Insert
+ Book add(Book book);
- /**
- * Saves changes made to an author's details.
- */
- @Update
- void update(Author author);
+ /**
+ * Saves changes made to an author's details.
+ */
+ @Update
+ void update(Author author);
- /**
- * Permanently removes a book from the library.
- */
- @Delete
- void remove(Book book);
+ /**
+ * Permanently removes a book from the library.
+ */
+ @Delete
+ void remove(Book book);
}
diff --git a/src/main/resources/db/migration/V0.0.0__Schema.sql b/src/main/resources/db/migration/V0.0.0__Schema.sql
index 8fffa28..5540a9d 100644
--- a/src/main/resources/db/migration/V0.0.0__Schema.sql
+++ b/src/main/resources/db/migration/V0.0.0__Schema.sql
@@ -3,68 +3,76 @@
-- --------------------------------------------------------
-- Stores the publishing companies.
-- Created first because Books depend on Publishers.
-CREATE TABLE IF NOT EXISTS Publisher (
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
- name VARCHAR(255) NOT NULL
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+CREATE TABLE IF NOT EXISTS Publisher
+(
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL
+) ENGINE = InnoDB
+ DEFAULT CHARSET = utf8mb4;
-- --------------------------------------------------------
-- 2. Book Table
-- --------------------------------------------------------
-- Stores the physical items (Books, Magazines, etc).
-- 'publisher_id' links to the Publisher table.
-CREATE TABLE IF NOT EXISTS Book (
- isbn VARCHAR(255) NOT NULL PRIMARY KEY,
- title VARCHAR(255) NOT NULL,
+CREATE TABLE IF NOT EXISTS Book
+(
+ isbn VARCHAR(255) NOT NULL PRIMARY KEY,
+ title VARCHAR(255) NOT NULL,
publicationDate DATE,
-- @Lob maps to TEXT or LONGTEXT in MySQL for large content
- text LONGTEXT NOT NULL,
+ text LONGTEXT NOT NULL,
-- Enum values stored as strings for readability
- type ENUM('NOVEL', 'BIOGRAPHY', 'TEXTBOOK', 'MAGAZINE', 'JOURNAL') NOT NULL,
+ type ENUM ('NOVEL', 'BIOGRAPHY', 'TEXTBOOK', 'MAGAZINE', 'JOURNAL') NOT NULL,
-- Foreign Key column
- publisher_id BIGINT,
+ publisher_id BIGINT,
CONSTRAINT fk_book_publisher
- FOREIGN KEY (publisher_id)
- REFERENCES Publisher(id)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+ FOREIGN KEY (publisher_id)
+ REFERENCES Publisher (id)
+) ENGINE = InnoDB
+ DEFAULT CHARSET = utf8mb4;
-- --------------------------------------------------------
-- 3. Author Table
-- --------------------------------------------------------
-- Stores author details.
-- The Address fields (street, city, zip) are embedded directly here.
-CREATE TABLE IF NOT EXISTS Author (
- ssn VARCHAR(255) NOT NULL PRIMARY KEY,
- name VARCHAR(255) NOT NULL,
+CREATE TABLE IF NOT EXISTS Author
+(
+ ssn VARCHAR(255) NOT NULL PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
-- Embedded Address fields
street VARCHAR(255),
- city VARCHAR(255),
- zip VARCHAR(255)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+ city VARCHAR(255),
+ zip VARCHAR(255)
+) ENGINE = InnoDB
+ DEFAULT CHARSET = utf8mb4;
-- --------------------------------------------------------
-- 4. Author_Book (Join Table)
-- --------------------------------------------------------
-- Handles the Many-to-Many relationship between Authors and Books.
-- One author can write many books, and one book can have multiple authors.
-CREATE TABLE IF NOT EXISTS Author_Book (
- authors_ssn VARCHAR(255) NOT NULL,
- books_isbn VARCHAR(255) NOT NULL,
+CREATE TABLE IF NOT EXISTS Author_Book
+(
+ authors_ssn VARCHAR(255) NOT NULL,
+ books_isbn VARCHAR(255) NOT NULL,
PRIMARY KEY (authors_ssn, books_isbn),
CONSTRAINT fk_ab_author
- FOREIGN KEY (authors_ssn)
- REFERENCES Author(ssn)
- ON DELETE CASCADE,
+ FOREIGN KEY (authors_ssn)
+ REFERENCES Author (ssn)
+ ON DELETE CASCADE,
CONSTRAINT fk_ab_book
- FOREIGN KEY (books_isbn)
- REFERENCES Book(isbn)
- ON DELETE CASCADE
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+ FOREIGN KEY (books_isbn)
+ REFERENCES Book (isbn)
+ ON DELETE CASCADE
+) ENGINE = InnoDB
+ DEFAULT CHARSET = utf8mb4;
diff --git a/src/main/resources/db/migration/V0.0.1__Sample_Data.sql b/src/main/resources/db/migration/V0.0.1__Sample_Data.sql
index fc8cb65..5ea66fa 100644
--- a/src/main/resources/db/migration/V0.0.1__Sample_Data.sql
+++ b/src/main/resources/db/migration/V0.0.1__Sample_Data.sql
@@ -3,11 +3,16 @@
-- --------------------------------------------------------
-- 1. Insert Publishers
-INSERT INTO Publisher (id, name) VALUES (1, 'Penguin Classics');
-INSERT INTO Publisher (id, name) VALUES (2, 'Bloomsbury Publishing');
-INSERT INTO Publisher (id, name) VALUES (3, 'Allen & Unwin');
-INSERT INTO Publisher (id, name) VALUES (4, 'Time USA, LLC');
-INSERT INTO Publisher (id, name) VALUES (5, 'Massachusetts Medical Society');
+INSERT INTO Publisher (id, name)
+VALUES (1, 'Penguin Classics');
+INSERT INTO Publisher (id, name)
+VALUES (2, 'Bloomsbury Publishing');
+INSERT INTO Publisher (id, name)
+VALUES (3, 'Allen & Unwin');
+INSERT INTO Publisher (id, name)
+VALUES (4, 'Time USA, LLC');
+INSERT INTO Publisher (id, name)
+VALUES (5, 'Massachusetts Medical Society');
-- 2. Insert Authors
-- Note: Address fields are embedded directly in the Author table
@@ -36,7 +41,8 @@ VALUES ('978-0441172719', 'Dune', '1965-08-01', 'In the week before their depart
-- Novel (J.K. Rowling)
INSERT INTO Book (isbn, title, publicationDate, text, type, publisher_id)
-VALUES ('978-0747532743', 'Harry Potter and the Philosopher\'s Stone', '1997-06-26', 'Mr. and Mrs. Dursley, of number four, Privet Drive...', 'NOVEL', 2);
+VALUES ('978-0747532743', 'Harry Potter and the Philosopher\'s Stone', '1997-06-26', 'Mr. and Mrs. Dursley, of number
+ four, Privet Drive...', 'NOVEL', 2);
-- Novel (J.R.R. Tolkien)
INSERT INTO Book (isbn, title, publicationDate, text, type, publisher_id)
diff --git a/src/main/resources/docs/full.adoc b/src/main/resources/docs/full.adoc
index 360bd3f..9e7aad4 100644
--- a/src/main/resources/docs/full.adoc
+++ b/src/main/resources/docs/full.adoc
@@ -16,13 +16,11 @@ All requests start with: `{{ server(0).url }}/library`.
{{ statusCode({ 200: "Default/Success", 201: "Created", 404: "Not found", 400: "Invalid Request"}) | list }}
-{% for tag in tags %}
-== {{ tag.name }}
+{% for tag in tags %} == {{ tag.name }}
{{ tag.description }}
-{% for route in tag.routes %}
-=== {{ loop.index + 1 }}. {{ route.summary }}
+{% for route in tag.routes %} === {{ loop.index + 1 }}. {{ route.summary }}
{{ route.description }}
@@ -46,17 +44,18 @@ Returns a {{ route | response | link }} object.
.Response
{{ route | response | example | json }}
-{% if route.security is not empty %}
-.Required Permissions
+{% if route.security is not empty %} .Required Permissions
+
[cols="1,3"]
|===
|Type | Scopes
{% for scheme in route.security %}
- {% for req in scheme %}
+{% for req in scheme %}
| *{{ req.key }}* | {{ req.value | join(", ") }}
- {% endfor %}
+{% endfor %}
{% endfor %}
|===
+
{% endif -%}
{% endfor -%}
{% endfor %}
@@ -64,13 +63,18 @@ Returns a {{ route | response | link }} object.
== 📖 Reference
=== Book Types
-When viewing book details, you will see a `type` field. Here is what those categories mean:
+
+When viewing book details, you will see a `type` field.
+Here is what those categories mean:
{{schema("Book.type") | table }}
=== Schemas
+
{% for schema in schemas %}
+
[id="{{ schema.name }}"]
==== {{ schema.name }}
+
{{ schema | table }}
{% endfor %}
diff --git a/src/main/resources/docs/index.adoc b/src/main/resources/docs/index.adoc
index 35b5b72..6e8be98 100644
--- a/src/main/resources/docs/index.adoc
+++ b/src/main/resources/docs/index.adoc
@@ -9,12 +9,12 @@
All requests start with: `{{ server(0).url }}/library`
-
== 🔍 Finding & Browsing Books
+
{%- set quickSearch= GET("/library/search") -%}
-{%- set paginated = GET("/library/books") -%}
-{%- set book = GET("/library/books/{isbn}") -%}
-{%- set addBook = POST("/library/books") %}
+{%- set paginated = GET("/library/books") -%}
+{%- set book = GET("/library/books/{isbn}") -%}
+{%- set addBook = POST("/library/books") %}
=== 1. {{ quickSearch.summary }}
@@ -67,7 +67,9 @@ All requests start with: `{{ server(0).url }}/library`
{{ addBook | response(400) | json }}
== 📖 Reference: Book Types
-When viewing book details, you will see a `type` field. Here is what those categories mean:
+
+When viewing book details, you will see a `type` field.
+Here is what those categories mean:
{{schema("Book.type") | table }}
diff --git a/src/main/resources/docs/tryIt.adoc b/src/main/resources/docs/tryIt.adoc
index d2c13e1..f0b246b 100644
--- a/src/main/resources/docs/tryIt.adoc
+++ b/src/main/resources/docs/tryIt.adoc
@@ -33,29 +33,24 @@ All requests start with: `{{ server(0).url }}/library`
*Endpoint:* `{{ route.method }} {{ route.path }}`
{# 1. Path Parameters Section (filtered) #}
-{% if route | parameters("path") is not empty %}
-.Path Parameters
+{% if route | parameters("path") is not empty %} .Path Parameters
{{ route | parameters("path") | table }}
{% endif %}
{# 2. Query Parameters Section (filtered) #}
-{% if route | parameters("query") is not empty %}
-.Query Parameters
+{% if route | parameters("query") is not empty %} .Query Parameters
{{ route | parameters("query") | table }}
{% endif %}
{# 3. Request Body (only for POST/PUT) #}
-{% if route.body is not null %}
-.Request Body
+{% if route.body is not null %} .Request Body
{{ route | request | body | example | json }}
{% endif %}
-{# 4. Response Example #}
-.Response (200 OK)
+{# 4. Response Example #} .Response (200 OK)
{{ route | response(200) | body | example | json }}
-{# 5. Usage Example (cURL) #}
-.Try it
+{# 5. Usage Example (cURL) #} .Try it
{{ route | curl(language="bash") }}
{% endfor %}