From 604c26c2c3e1f4b18e881062269feefcf7318ba2 Mon Sep 17 00:00:00 2001 From: Hanna Adenholm Date: Tue, 26 Aug 2025 13:21:23 +0200 Subject: [PATCH] Finished the exercise core and extension --- build.gradle | 13 ++- src/main/docker/Docker-compose.yml | 28 ++++++ src/main/docker/Dockerfile | 9 ++ src/main/java/com/booleanuk/api/Main.java | 11 +++ .../api/controller/AuthorController.java | 59 +++++++++++++ .../api/controller/BookController.java | 72 +++++++++++++++ .../api/controller/PublisherController.java | 55 ++++++++++++ .../java/com/booleanuk/api/model/Author.java | 88 +++++++++++++++++++ .../java/com/booleanuk/api/model/Book.java | 78 ++++++++++++++++ .../com/booleanuk/api/model/Publisher.java | 64 ++++++++++++++ .../api/repository/AuthorRepository.java | 7 ++ .../api/repository/BookRepository.java | 7 ++ .../api/repository/PublisherRepository.java | 7 ++ 13 files changed, 497 insertions(+), 1 deletion(-) create mode 100644 src/main/docker/Docker-compose.yml create mode 100644 src/main/docker/Dockerfile create mode 100644 src/main/java/com/booleanuk/api/Main.java create mode 100644 src/main/java/com/booleanuk/api/controller/AuthorController.java create mode 100644 src/main/java/com/booleanuk/api/controller/BookController.java create mode 100644 src/main/java/com/booleanuk/api/controller/PublisherController.java create mode 100644 src/main/java/com/booleanuk/api/model/Author.java create mode 100644 src/main/java/com/booleanuk/api/model/Book.java create mode 100644 src/main/java/com/booleanuk/api/model/Publisher.java create mode 100644 src/main/java/com/booleanuk/api/repository/AuthorRepository.java create mode 100644 src/main/java/com/booleanuk/api/repository/BookRepository.java create mode 100644 src/main/java/com/booleanuk/api/repository/PublisherRepository.java diff --git a/build.gradle b/build.gradle index fb070c4..696bb4a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.1.4' + id 'org.springframework.boot' version '3.5.5' id 'io.spring.dependency-management' version '1.1.3' } @@ -31,3 +31,14 @@ dependencies { tasks.named('test') { useJUnitPlatform() } + +jar { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + manifest { + attributes 'Main-Class': 'com.booleanuk.api.Main' + } + + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} diff --git a/src/main/docker/Docker-compose.yml b/src/main/docker/Docker-compose.yml new file mode 100644 index 0000000..2937f03 --- /dev/null +++ b/src/main/docker/Docker-compose.yml @@ -0,0 +1,28 @@ +services: + app: + image: 'book-app:latest' + container_name: app + depends_on: + - db + ports: + - '4000:4000' + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/mypostgresuser + - SPRING_DATASOURCE_USERNAME=mypostgresuser + - SPRING_DATASOURCE_PASSWORD=mypostgrespassword + - SPRING_JPA_HIBERNATE_DDL_AUTO=update + + db: + image: 'postgres:latest' + container_name: db + ports: + - '5432:5432' + volumes: + - db-data:/var/lib/postgresql/data + environment: + - POSTGRES_USER=mypostgresuser + - POSTGRES_DATABASE=mypostgresuser + - POSTGRES_PASSWORD=mypostgrespassword + +volumes: + db-data: \ No newline at end of file diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile new file mode 100644 index 0000000..c28337f --- /dev/null +++ b/src/main/docker/Dockerfile @@ -0,0 +1,9 @@ +FROM mcr.microsoft.com/openjdk/jdk:21-ubuntu + +WORKDIR /app + +COPY java.docker.day.2-0.0.1.jar /app/java.docker.day.2-0.0.1.jar + +EXPOSE 4000 + +ENTRYPOINT [ "java", "-jar", "java.docker.day.2-0.0.1.jar" ] \ No newline at end of file diff --git a/src/main/java/com/booleanuk/api/Main.java b/src/main/java/com/booleanuk/api/Main.java new file mode 100644 index 0000000..8e749e0 --- /dev/null +++ b/src/main/java/com/booleanuk/api/Main.java @@ -0,0 +1,11 @@ +package com.booleanuk.api; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Main { + public static void main(String[] args) { + SpringApplication.run(Main.class, args); + } +} diff --git a/src/main/java/com/booleanuk/api/controller/AuthorController.java b/src/main/java/com/booleanuk/api/controller/AuthorController.java new file mode 100644 index 0000000..00805d1 --- /dev/null +++ b/src/main/java/com/booleanuk/api/controller/AuthorController.java @@ -0,0 +1,59 @@ +package com.booleanuk.api.controller; + +import com.booleanuk.api.model.Author; +import com.booleanuk.api.repository.AuthorRepository; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ResponseStatusException; + +import java.util.List; + +@RestController +@RequestMapping("authors") +public class AuthorController { + private final AuthorRepository repository; + + public AuthorController(AuthorRepository repository) { + this.repository = repository; + } + + @GetMapping + public ResponseEntity> getAll() { + return ResponseEntity.ok(this.repository.findAll()); + } + + @GetMapping("{id}") + public ResponseEntity getById(@PathVariable("id") Integer id) { + Author author = this.repository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find author with that id.")); + return ResponseEntity.ok(author); + } + + record PostAuthor(String first_name, String last_name, String email, boolean alive) {} + + //@ResponseStatus(HttpStatus.CREATED) + @PostMapping + public ResponseEntity create(@RequestBody PostAuthor request) { + Author author = new Author(request.first_name(), request.last_name(), request.email(), request.alive()); + return new ResponseEntity<>(this.repository.save(author), HttpStatus.CREATED); + } + + @PutMapping("{id}") + public ResponseEntity updateAuthor(@PathVariable int id, @RequestBody PostAuthor author) { + Author authorToUpdate = this.repository.findById(id).orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find author with that id.")); + authorToUpdate.setFirstName(author.first_name()); + authorToUpdate.setLastName(author.last_name()); + authorToUpdate.setEmail(author.email()); + authorToUpdate.setAlive(author.alive()); + return new ResponseEntity<>(this.repository.save(authorToUpdate), HttpStatus.CREATED); + } + + @DeleteMapping("{id}") + public ResponseEntity deleteAuthor(@PathVariable int id) { + Author authorToDelete = this.repository.findById(id).orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find author with that id.")); + this.repository.delete(authorToDelete); + return ResponseEntity.ok(authorToDelete); + } +} diff --git a/src/main/java/com/booleanuk/api/controller/BookController.java b/src/main/java/com/booleanuk/api/controller/BookController.java new file mode 100644 index 0000000..396c72a --- /dev/null +++ b/src/main/java/com/booleanuk/api/controller/BookController.java @@ -0,0 +1,72 @@ +package com.booleanuk.api.controller; + +import com.booleanuk.api.model.Author; +import com.booleanuk.api.model.Book; +import com.booleanuk.api.model.Publisher; +import com.booleanuk.api.repository.AuthorRepository; +import com.booleanuk.api.repository.BookRepository; +import com.booleanuk.api.repository.PublisherRepository; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ResponseStatusException; + +import java.util.List; + +@RestController +@RequestMapping("books") +public class BookController { + private final BookRepository repository; + private final AuthorRepository authorRepository; + private final PublisherRepository publisherRepository; + + public BookController(BookRepository repository, AuthorRepository authorRepository, PublisherRepository publisherRepository) { + this.repository = repository; + this.authorRepository = authorRepository; + this.publisherRepository = publisherRepository; + + } + + @GetMapping + public ResponseEntity> getAll() { + return ResponseEntity.ok(this.repository.findAll()); + } + + @GetMapping("{id}") + public ResponseEntity getById(@PathVariable("id") Integer id) { + Book book = this.repository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find book with that id.")); + return ResponseEntity.ok(book); + } + + private record PostBook(String title, String genre, int author_id, int publisher_id) {} + + //@ResponseStatus(HttpStatus.CREATED) + @PostMapping + public ResponseEntity create(@RequestBody PostBook request) { + Author author = authorRepository.findById(request.author_id()).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find author with that id.")); + Publisher publisher = publisherRepository.findById(request.publisher_id()).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find publisher with that id.")); + Book book = new Book(request.title(), request.genre(), author, publisher); + return new ResponseEntity<>(this.repository.save(book), HttpStatus.CREATED); + } + + @PutMapping("{id}") + public ResponseEntity updateBook(@PathVariable int id, @RequestBody PostBook book) { + Book bookToUpdate = this.repository.findById(id).orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find book with that id.")); + Author author = authorRepository.findById(book.author_id()).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find author with that id.")); + Publisher publisher = publisherRepository.findById(book.publisher_id()).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find publisher with that id.")); + bookToUpdate.setTitle(book.title()); + bookToUpdate.setGenre(book.genre()); + bookToUpdate.setAuthor(author); + bookToUpdate.setPublisher(publisher); + return new ResponseEntity<>(this.repository.save(bookToUpdate), HttpStatus.CREATED); + } + + @DeleteMapping("{id}") + public ResponseEntity deleteBook(@PathVariable int id) { + Book bookToDelete = this.repository.findById(id).orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find book with that id.")); + this.repository.delete(bookToDelete); + return ResponseEntity.ok(bookToDelete); + } +} diff --git a/src/main/java/com/booleanuk/api/controller/PublisherController.java b/src/main/java/com/booleanuk/api/controller/PublisherController.java new file mode 100644 index 0000000..70de070 --- /dev/null +++ b/src/main/java/com/booleanuk/api/controller/PublisherController.java @@ -0,0 +1,55 @@ +package com.booleanuk.api.controller; + +import com.booleanuk.api.model.Publisher; +import com.booleanuk.api.repository.PublisherRepository; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ResponseStatusException; + +import java.util.List; + +@RestController +@RequestMapping("publishers") +public class PublisherController { + private final PublisherRepository repository; + + public PublisherController(PublisherRepository repository) { + this.repository = repository; + } + + @GetMapping + public ResponseEntity> getAll() { + return ResponseEntity.ok(this.repository.findAll()); + } + + @GetMapping("{id}") + public ResponseEntity getById(@PathVariable("id") Integer id) { + Publisher publisher = this.repository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find publisher with that id.")); + return ResponseEntity.ok(publisher); + } + + //@ResponseStatus(HttpStatus.CREATED) + @PostMapping + public ResponseEntity create(@RequestBody Publisher request) { + Publisher publisher = new Publisher(request.getName(), request.getLocation()); + return new ResponseEntity<>(this.repository.save(publisher), HttpStatus.CREATED); + } + + @PutMapping("{id}") + public ResponseEntity updatePublisher(@PathVariable int id, @RequestBody Publisher publisher) { + Publisher publisherToUpdate = this.repository.findById(id).orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find publisher with that id.")); + publisherToUpdate.setName(publisher.getName()); + publisherToUpdate.setLocation(publisher.getLocation()); + return new ResponseEntity<>(this.repository.save(publisherToUpdate), HttpStatus.CREATED); + } + + @DeleteMapping("{id}") + public ResponseEntity deletePublisher(@PathVariable int id) { + Publisher publisherToDelete = this.repository.findById(id).orElseThrow( + () -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find publisher with that id.")); + this.repository.delete(publisherToDelete); + return ResponseEntity.ok(publisherToDelete); + } +} diff --git a/src/main/java/com/booleanuk/api/model/Author.java b/src/main/java/com/booleanuk/api/model/Author.java new file mode 100644 index 0000000..8f96bfe --- /dev/null +++ b/src/main/java/com/booleanuk/api/model/Author.java @@ -0,0 +1,88 @@ +package com.booleanuk.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.persistence.*; + +import java.util.List; + +@Entity +@Table(name="authors") +public class Author { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @Column(name="first_name") + private String firstName; + + @Column(name="last_name") + private String lastName; + + @Column + private String email; + + @Column + private boolean alive; + + @OneToMany(mappedBy = "author") + @JsonIgnoreProperties({"author"}) + private List books; + + public Author() { + } + + public Author(String firstName, String lastName, String email, boolean alive) { + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.alive = alive; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public boolean isAlive() { + return alive; + } + + public void setAlive(boolean alive) { + this.alive = alive; + } + + public List getBooks() { + return books; + } + + public void setBooks(List books) { + this.books = books; + } +} diff --git a/src/main/java/com/booleanuk/api/model/Book.java b/src/main/java/com/booleanuk/api/model/Book.java new file mode 100644 index 0000000..08006b8 --- /dev/null +++ b/src/main/java/com/booleanuk/api/model/Book.java @@ -0,0 +1,78 @@ +package com.booleanuk.api.model; + +import com.fasterxml.jackson.annotation.JsonIncludeProperties; +import jakarta.persistence.*; + +@Entity +@Table(name="books") +public class Book { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @Column + private String title; + + @Column + private String genre; + + @ManyToOne + @JoinColumn(name = "author_id", nullable = false) + @JsonIncludeProperties({"id"}) + private Author author; + + @ManyToOne + @JoinColumn(name = "publisher_id", nullable = false) + @JsonIncludeProperties({"id"}) + private Publisher publisher; + + public Book() { + } + + public Book(String title, String genre, Author author, Publisher publisher) { + this.title = title; + this.genre = genre; + this.author = author; + this.publisher = publisher; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getGenre() { + return genre; + } + + public void setGenre(String genre) { + this.genre = genre; + } + + public Author getAuthor() { + return author; + } + + public void setAuthor(Author author) { + this.author = author; + } + + public Publisher getPublisher() { + return publisher; + } + + public void setPublisher(Publisher publisher) { + this.publisher = publisher; + } +} diff --git a/src/main/java/com/booleanuk/api/model/Publisher.java b/src/main/java/com/booleanuk/api/model/Publisher.java new file mode 100644 index 0000000..1667567 --- /dev/null +++ b/src/main/java/com/booleanuk/api/model/Publisher.java @@ -0,0 +1,64 @@ +package com.booleanuk.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.persistence.*; + +import java.util.List; + +@Entity +@Table(name="publishers") +public class Publisher { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @Column + private String name; + + @Column + private String location; + + @OneToMany(mappedBy = "publisher") + @JsonIgnoreProperties({"publisher"}) + private List books; + + public Publisher() { + } + + public Publisher(String name, String location) { + this.name = name; + this.location = location; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public List getBooks() { + return books; + } + + public void setBooks(List books) { + this.books = books; + } +} diff --git a/src/main/java/com/booleanuk/api/repository/AuthorRepository.java b/src/main/java/com/booleanuk/api/repository/AuthorRepository.java new file mode 100644 index 0000000..a14d313 --- /dev/null +++ b/src/main/java/com/booleanuk/api/repository/AuthorRepository.java @@ -0,0 +1,7 @@ +package com.booleanuk.api.repository; + +import com.booleanuk.api.model.Author; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AuthorRepository extends JpaRepository { +} diff --git a/src/main/java/com/booleanuk/api/repository/BookRepository.java b/src/main/java/com/booleanuk/api/repository/BookRepository.java new file mode 100644 index 0000000..6d6f2fb --- /dev/null +++ b/src/main/java/com/booleanuk/api/repository/BookRepository.java @@ -0,0 +1,7 @@ +package com.booleanuk.api.repository; + +import com.booleanuk.api.model.Book; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BookRepository extends JpaRepository { +} diff --git a/src/main/java/com/booleanuk/api/repository/PublisherRepository.java b/src/main/java/com/booleanuk/api/repository/PublisherRepository.java new file mode 100644 index 0000000..5f32e5f --- /dev/null +++ b/src/main/java/com/booleanuk/api/repository/PublisherRepository.java @@ -0,0 +1,7 @@ +package com.booleanuk.api.repository; + +import com.booleanuk.api.model.Publisher; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PublisherRepository extends JpaRepository { +}