From ebd92ee8f1dbc8ff2ca2db05d14e8fd99b47fe8c Mon Sep 17 00:00:00 2001 From: Frida Anselin Date: Mon, 3 Feb 2025 10:34:46 +0100 Subject: [PATCH 1/7] Add basic core file structure --- .gitignore | 2 ++ build.gradle | 17 ++++++++++++++--- src/main/java/com/booleanuk/api/Main.java | 11 +++++++++++ .../api/controller/PostController.java | 4 ++++ .../api/controller/UserController.java | 4 ++++ src/main/java/com/booleanuk/api/model/Post.java | 4 ++++ src/main/java/com/booleanuk/api/model/User.java | 4 ++++ .../api/repository/PostRepository.java | 4 ++++ .../api/repository/UserRepository.java | 4 ++++ .../booleanuk/api/response/ErrorResponse.java | 4 ++++ .../api/response/PostListResponse.java | 4 ++++ .../booleanuk/api/response/PostResponse.java | 4 ++++ .../com/booleanuk/api/response/Response.java | 4 ++++ .../api/response/UserListResponse.java | 4 ++++ .../booleanuk/api/response/UserResponse.java | 4 ++++ 15 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/booleanuk/api/Main.java create mode 100644 src/main/java/com/booleanuk/api/controller/PostController.java create mode 100644 src/main/java/com/booleanuk/api/controller/UserController.java create mode 100644 src/main/java/com/booleanuk/api/model/Post.java create mode 100644 src/main/java/com/booleanuk/api/model/User.java create mode 100644 src/main/java/com/booleanuk/api/repository/PostRepository.java create mode 100644 src/main/java/com/booleanuk/api/repository/UserRepository.java create mode 100644 src/main/java/com/booleanuk/api/response/ErrorResponse.java create mode 100644 src/main/java/com/booleanuk/api/response/PostListResponse.java create mode 100644 src/main/java/com/booleanuk/api/response/PostResponse.java create mode 100644 src/main/java/com/booleanuk/api/response/Response.java create mode 100644 src/main/java/com/booleanuk/api/response/UserListResponse.java create mode 100644 src/main/java/com/booleanuk/api/response/UserResponse.java diff --git a/.gitignore b/.gitignore index c2065bc..b3db3e2 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ out/ ### VS Code ### .vscode/ + +application.yml diff --git a/build.gradle b/build.gradle index fb070c4..5f94887 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.1.4' - id 'io.spring.dependency-management' version '1.1.3' + id 'org.springframework.boot' version '3.4.1' + id 'io.spring.dependency-management' version '1.1.7' } group = 'com.booleanuk' @@ -23,7 +23,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' - implementation 'org.postgresql:postgresql:42.6.0' + runtimeOnly 'org.postgresql:postgresql' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' } @@ -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) } + } +} \ 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..4e437d6 --- /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); + } +} \ No newline at end of file diff --git a/src/main/java/com/booleanuk/api/controller/PostController.java b/src/main/java/com/booleanuk/api/controller/PostController.java new file mode 100644 index 0000000..e6861dc --- /dev/null +++ b/src/main/java/com/booleanuk/api/controller/PostController.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.controller; + +public class PostController { +} diff --git a/src/main/java/com/booleanuk/api/controller/UserController.java b/src/main/java/com/booleanuk/api/controller/UserController.java new file mode 100644 index 0000000..a849ff0 --- /dev/null +++ b/src/main/java/com/booleanuk/api/controller/UserController.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.controller; + +public class UserController { +} diff --git a/src/main/java/com/booleanuk/api/model/Post.java b/src/main/java/com/booleanuk/api/model/Post.java new file mode 100644 index 0000000..db4d9ca --- /dev/null +++ b/src/main/java/com/booleanuk/api/model/Post.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.model; + +public class Post { +} diff --git a/src/main/java/com/booleanuk/api/model/User.java b/src/main/java/com/booleanuk/api/model/User.java new file mode 100644 index 0000000..050b121 --- /dev/null +++ b/src/main/java/com/booleanuk/api/model/User.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.model; + +public class User { +} diff --git a/src/main/java/com/booleanuk/api/repository/PostRepository.java b/src/main/java/com/booleanuk/api/repository/PostRepository.java new file mode 100644 index 0000000..6d9726c --- /dev/null +++ b/src/main/java/com/booleanuk/api/repository/PostRepository.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.repository; + +public class PostRepository { +} diff --git a/src/main/java/com/booleanuk/api/repository/UserRepository.java b/src/main/java/com/booleanuk/api/repository/UserRepository.java new file mode 100644 index 0000000..80559ce --- /dev/null +++ b/src/main/java/com/booleanuk/api/repository/UserRepository.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.repository; + +public class UserRepository { +} diff --git a/src/main/java/com/booleanuk/api/response/ErrorResponse.java b/src/main/java/com/booleanuk/api/response/ErrorResponse.java new file mode 100644 index 0000000..1d47681 --- /dev/null +++ b/src/main/java/com/booleanuk/api/response/ErrorResponse.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.response; + +public class ErrorResponse { +} diff --git a/src/main/java/com/booleanuk/api/response/PostListResponse.java b/src/main/java/com/booleanuk/api/response/PostListResponse.java new file mode 100644 index 0000000..c55a5ad --- /dev/null +++ b/src/main/java/com/booleanuk/api/response/PostListResponse.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.response; + +public class PostListResponse { +} diff --git a/src/main/java/com/booleanuk/api/response/PostResponse.java b/src/main/java/com/booleanuk/api/response/PostResponse.java new file mode 100644 index 0000000..797a767 --- /dev/null +++ b/src/main/java/com/booleanuk/api/response/PostResponse.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.response; + +public class PostResponse { +} diff --git a/src/main/java/com/booleanuk/api/response/Response.java b/src/main/java/com/booleanuk/api/response/Response.java new file mode 100644 index 0000000..eacd7f5 --- /dev/null +++ b/src/main/java/com/booleanuk/api/response/Response.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.response; + +public class Response { +} diff --git a/src/main/java/com/booleanuk/api/response/UserListResponse.java b/src/main/java/com/booleanuk/api/response/UserListResponse.java new file mode 100644 index 0000000..16a6278 --- /dev/null +++ b/src/main/java/com/booleanuk/api/response/UserListResponse.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.response; + +public class UserListResponse { +} diff --git a/src/main/java/com/booleanuk/api/response/UserResponse.java b/src/main/java/com/booleanuk/api/response/UserResponse.java new file mode 100644 index 0000000..af8f7d6 --- /dev/null +++ b/src/main/java/com/booleanuk/api/response/UserResponse.java @@ -0,0 +1,4 @@ +package com.booleanuk.api.response; + +public class UserResponse { +} From 74422059420552e14e66ae25055c97e31b0ed662 Mon Sep 17 00:00:00 2001 From: Frida Anselin Date: Mon, 3 Feb 2025 11:57:34 +0100 Subject: [PATCH 2/7] Add core responses, controller, models, repositories --- .../api/controller/PostController.java | 89 +++++++++++++++++++ .../api/controller/UserController.java | 78 ++++++++++++++++ .../java/com/booleanuk/api/model/Post.java | 41 +++++++++ .../java/com/booleanuk/api/model/User.java | 35 ++++++++ .../api/repository/PostRepository.java | 5 +- .../api/repository/UserRepository.java | 5 +- .../booleanuk/api/response/ErrorResponse.java | 17 +++- .../api/response/PostListResponse.java | 6 +- .../booleanuk/api/response/PostResponse.java | 4 +- .../com/booleanuk/api/response/Response.java | 12 ++- .../api/response/UserListResponse.java | 6 +- .../booleanuk/api/response/UserResponse.java | 4 +- 12 files changed, 294 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/booleanuk/api/controller/PostController.java b/src/main/java/com/booleanuk/api/controller/PostController.java index e6861dc..e6e72d3 100644 --- a/src/main/java/com/booleanuk/api/controller/PostController.java +++ b/src/main/java/com/booleanuk/api/controller/PostController.java @@ -1,4 +1,93 @@ package com.booleanuk.api.controller; +import com.booleanuk.api.model.Post; +import com.booleanuk.api.model.User; +import com.booleanuk.api.repository.PostRepository; +import com.booleanuk.api.repository.UserRepository; +import com.booleanuk.api.response.ErrorResponse; +import com.booleanuk.api.response.PostListResponse; +import com.booleanuk.api.response.PostResponse; +import com.booleanuk.api.response.Response; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDateTime; + +@RestController +@RequestMapping("posts") public class PostController { + @Autowired + private PostRepository postRepository; + + @Autowired + private UserRepository userRepository; + + private ErrorResponse errorResponse = new ErrorResponse(); + private PostResponse postResponse = new PostResponse(); + private PostListResponse postListResponse = new PostListResponse(); + + @GetMapping + public ResponseEntity> getAllPosts() { + postListResponse.set(this.postRepository.findAll()); + return ResponseEntity.ok(postListResponse); + } + + @PostMapping + public ResponseEntity> createPost(@RequestBody Post post) { + post.setPosted(LocalDateTime.now()); + post.setUpdated(LocalDateTime.now()); + try { + postResponse.set(this.postRepository.save(post)); + } catch (Exception e) { + errorResponse.set("Bad request"); + return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + } + return new ResponseEntity<>(postResponse, HttpStatus.CREATED); + } + + @GetMapping("{id}") + public ResponseEntity> getOnePost(@PathVariable int id) { + Post post = this.postRepository.findById(id).orElse(null); + if (post == null) { + this.errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.postResponse.set(post); + return ResponseEntity.ok(postResponse); + } + + @PutMapping("{id}") + public ResponseEntity> updatePost(@PathVariable int id, @RequestBody Post post) { + Post postToUpdate = this.postRepository.findById(id).orElse(null); + if (postToUpdate == null) { + this.errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + postToUpdate.setTitle(post.getTitle()); + postToUpdate.setContent(post.getContent()); + postToUpdate.setUpdated(LocalDateTime.now()); + try { + postToUpdate = this.postRepository.save(postToUpdate); + } catch (Exception e) { + this.errorResponse.set("Bad request"); + return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + } + this.postResponse.set(postToUpdate); + return new ResponseEntity<>(postResponse, HttpStatus.CREATED); + } + + @DeleteMapping("{id}") + public ResponseEntity> deletePost(@PathVariable int id) { + Post postToDelete = this.postRepository.findById(id).orElse(null); + if (postToDelete == null) { + errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.postRepository.delete(postToDelete); + postResponse.set(postToDelete); + return ResponseEntity.ok(postResponse); + } + } diff --git a/src/main/java/com/booleanuk/api/controller/UserController.java b/src/main/java/com/booleanuk/api/controller/UserController.java index a849ff0..4e2b116 100644 --- a/src/main/java/com/booleanuk/api/controller/UserController.java +++ b/src/main/java/com/booleanuk/api/controller/UserController.java @@ -1,4 +1,82 @@ package com.booleanuk.api.controller; +import com.booleanuk.api.model.User; +import com.booleanuk.api.repository.UserRepository; +import com.booleanuk.api.response.ErrorResponse; +import com.booleanuk.api.response.Response; +import com.booleanuk.api.response.UserListResponse; +import com.booleanuk.api.response.UserResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("users") public class UserController { + @Autowired + private UserRepository userRepository; + + private ErrorResponse errorResponse = new ErrorResponse(); + private UserResponse userResponse = new UserResponse(); + private UserListResponse userListResponse = new UserListResponse(); + + @GetMapping + public ResponseEntity> getAllUsers() { + this.userListResponse.set(this.userRepository.findAll()); + return ResponseEntity.ok(userListResponse); + } + + @PostMapping + public ResponseEntity> createUser (@RequestBody User user) { + try { + this.userResponse.set(this.userRepository.save(user)); + } catch (Exception e) { + this.errorResponse.set("Bad request"); + return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + } + return new ResponseEntity<>(userResponse, HttpStatus.CREATED); + } + + @GetMapping("{id}") + public ResponseEntity> getOneUser(@PathVariable int id) { + User user = this.userRepository.findById(id).orElse(null); + if (user == null) { + this.errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.userResponse.set(user); + return ResponseEntity.ok(userResponse); + } + + @PutMapping("{id}") + public ResponseEntity> updateUser(@PathVariable int id, @RequestBody User user) { + User userToUpdate = this.userRepository.findById(id).orElse(null); + if (userToUpdate == null) { + this.errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + userToUpdate.setUsername(user.getUsername()); + userToUpdate.setEmail(user.getEmail()); + try { + userToUpdate = this.userRepository.save(userToUpdate); + } catch (Exception e) { + this.errorResponse.set("Bad request"); + return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + } + this.userResponse.set(userToUpdate); + return new ResponseEntity<>(userResponse, HttpStatus.CREATED); + } + + @DeleteMapping("{id}") + public ResponseEntity> deleteUser(@PathVariable int id) { + User userToDelete = this.userRepository.findById(id).orElse(null); + if (userToDelete == null) { + errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.userRepository.delete(userToDelete); + userResponse.set(userToDelete); + return ResponseEntity.ok(userResponse); + } } diff --git a/src/main/java/com/booleanuk/api/model/Post.java b/src/main/java/com/booleanuk/api/model/Post.java index db4d9ca..e181f88 100644 --- a/src/main/java/com/booleanuk/api/model/Post.java +++ b/src/main/java/com/booleanuk/api/model/Post.java @@ -1,4 +1,45 @@ package com.booleanuk.api.model; +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "posts") public class Post { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + @Column + private String title; + @Column + private String content; + @Column + @JsonFormat(pattern = "yyyy-MM-dd mm:ss") + private LocalDateTime posted; + @Column + @JsonFormat(pattern = "yyyy-MM-dd mm:ss") + private LocalDateTime updated; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + public Post(String title, String content) { + this.title = title; + this.content = content; + } + + public Post(int id) { + this.id = id; + } } diff --git a/src/main/java/com/booleanuk/api/model/User.java b/src/main/java/com/booleanuk/api/model/User.java index 050b121..83d9936 100644 --- a/src/main/java/com/booleanuk/api/model/User.java +++ b/src/main/java/com/booleanuk/api/model/User.java @@ -1,4 +1,39 @@ package com.booleanuk.api.model; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "users") public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + @Column + private String username; + @Column + private String email; + + @OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE) + @JsonIgnoreProperties("user") + private List posts; + + public User(String username, String email) { + this.username = username; + this.email = email; + } + + public User(int id) { + this.id = id; + } } diff --git a/src/main/java/com/booleanuk/api/repository/PostRepository.java b/src/main/java/com/booleanuk/api/repository/PostRepository.java index 6d9726c..a80e468 100644 --- a/src/main/java/com/booleanuk/api/repository/PostRepository.java +++ b/src/main/java/com/booleanuk/api/repository/PostRepository.java @@ -1,4 +1,7 @@ package com.booleanuk.api.repository; -public class PostRepository { +import com.booleanuk.api.model.Post; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { } diff --git a/src/main/java/com/booleanuk/api/repository/UserRepository.java b/src/main/java/com/booleanuk/api/repository/UserRepository.java index 80559ce..74858a5 100644 --- a/src/main/java/com/booleanuk/api/repository/UserRepository.java +++ b/src/main/java/com/booleanuk/api/repository/UserRepository.java @@ -1,4 +1,7 @@ package com.booleanuk.api.repository; -public class UserRepository { +import com.booleanuk.api.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { } diff --git a/src/main/java/com/booleanuk/api/response/ErrorResponse.java b/src/main/java/com/booleanuk/api/response/ErrorResponse.java index 1d47681..dbda74e 100644 --- a/src/main/java/com/booleanuk/api/response/ErrorResponse.java +++ b/src/main/java/com/booleanuk/api/response/ErrorResponse.java @@ -1,4 +1,19 @@ package com.booleanuk.api.response; -public class ErrorResponse { +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.HashMap; +import java.util.Map; + +@Getter +@NoArgsConstructor +public class ErrorResponse extends Response>{ + + public void set(String message) { + this.status = "error"; + Map reply = new HashMap<>(); + reply.put("message", message); + this.data = reply; + } } diff --git a/src/main/java/com/booleanuk/api/response/PostListResponse.java b/src/main/java/com/booleanuk/api/response/PostListResponse.java index c55a5ad..94da206 100644 --- a/src/main/java/com/booleanuk/api/response/PostListResponse.java +++ b/src/main/java/com/booleanuk/api/response/PostListResponse.java @@ -1,4 +1,8 @@ package com.booleanuk.api.response; -public class PostListResponse { +import com.booleanuk.api.model.Post; + +import java.util.List; + +public class PostListResponse extends Response> { } diff --git a/src/main/java/com/booleanuk/api/response/PostResponse.java b/src/main/java/com/booleanuk/api/response/PostResponse.java index 797a767..11ad6d6 100644 --- a/src/main/java/com/booleanuk/api/response/PostResponse.java +++ b/src/main/java/com/booleanuk/api/response/PostResponse.java @@ -1,4 +1,6 @@ package com.booleanuk.api.response; -public class PostResponse { +import com.booleanuk.api.model.Post; + +public class PostResponse extends Response { } diff --git a/src/main/java/com/booleanuk/api/response/Response.java b/src/main/java/com/booleanuk/api/response/Response.java index eacd7f5..0748976 100644 --- a/src/main/java/com/booleanuk/api/response/Response.java +++ b/src/main/java/com/booleanuk/api/response/Response.java @@ -1,4 +1,14 @@ package com.booleanuk.api.response; -public class Response { +import lombok.Getter; + +@Getter +public class Response { + protected String status; + protected T data; + + public void set(T data) { + this.status = "success"; + this.data = data; + } } diff --git a/src/main/java/com/booleanuk/api/response/UserListResponse.java b/src/main/java/com/booleanuk/api/response/UserListResponse.java index 16a6278..8a54e31 100644 --- a/src/main/java/com/booleanuk/api/response/UserListResponse.java +++ b/src/main/java/com/booleanuk/api/response/UserListResponse.java @@ -1,4 +1,8 @@ package com.booleanuk.api.response; -public class UserListResponse { +import com.booleanuk.api.model.User; + +import java.util.List; + +public class UserListResponse extends Response> { } diff --git a/src/main/java/com/booleanuk/api/response/UserResponse.java b/src/main/java/com/booleanuk/api/response/UserResponse.java index af8f7d6..5dda424 100644 --- a/src/main/java/com/booleanuk/api/response/UserResponse.java +++ b/src/main/java/com/booleanuk/api/response/UserResponse.java @@ -1,4 +1,6 @@ package com.booleanuk.api.response; -public class UserResponse { +import com.booleanuk.api.model.User; + +public class UserResponse extends Response { } From 1b7cee48a422078d8270006fdea8180d2cd7a170 Mon Sep 17 00:00:00 2001 From: Frida Anselin Date: Mon, 3 Feb 2025 14:13:15 +0100 Subject: [PATCH 3/7] Add docker compose, core done --- Docker/Dockerfile | 9 +++++++++ Docker/docker-compose.yml | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 Docker/Dockerfile create mode 100644 Docker/docker-compose.yml diff --git a/Docker/Dockerfile b/Docker/Dockerfile new file mode 100644 index 0000000..c28337f --- /dev/null +++ b/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/Docker/docker-compose.yml b/Docker/docker-compose.yml new file mode 100644 index 0000000..f43ba67 --- /dev/null +++ b/Docker/docker-compose.yml @@ -0,0 +1,23 @@ +services: + app: + image: "blogging-box: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" + environment: + - POSTGRES_USER=mypostgresuser + - POSTGRES_DATABASE=mypostgresuser + - POSTGRES_PASSWORD=mypostgrespassword \ No newline at end of file From a88130de7b1b66a420492e3fff4a26deb9d36593 Mon Sep 17 00:00:00 2001 From: Frida Anselin Date: Mon, 3 Feb 2025 15:11:00 +0100 Subject: [PATCH 4/7] Add friends --- .../api/controller/UserController.java | 53 ++++++++++++++++++- .../java/com/booleanuk/api/model/User.java | 5 ++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/booleanuk/api/controller/UserController.java b/src/main/java/com/booleanuk/api/controller/UserController.java index 4e2b116..496340b 100644 --- a/src/main/java/com/booleanuk/api/controller/UserController.java +++ b/src/main/java/com/booleanuk/api/controller/UserController.java @@ -72,11 +72,60 @@ public ResponseEntity> updateUser(@PathVariable int id, @RequestBody public ResponseEntity> deleteUser(@PathVariable int id) { User userToDelete = this.userRepository.findById(id).orElse(null); if (userToDelete == null) { - errorResponse.set("Not found"); + this.errorResponse.set("Not found"); return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); } this.userRepository.delete(userToDelete); - userResponse.set(userToDelete); + this.userResponse.set(userToDelete); + return ResponseEntity.ok(userResponse); + } + + @GetMapping("{id}/friends") + public ResponseEntity> getAllFriends(@PathVariable int id) { + User userWithFriends = this.userRepository.findById(id).orElse(null); + if (userWithFriends == null) { + errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.userListResponse.set(userWithFriends.getFriends()); + return ResponseEntity.ok(userListResponse); + } + + @PostMapping("{id}/friends") + public ResponseEntity> addFriend(@PathVariable int id, @RequestBody User friend) { + User userWithFriends = this.userRepository.findById(id).orElse(null); + if (userWithFriends == null) { + this.errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + userWithFriends.getFriends().add(friend); + this.userResponse.set(friend); + return ResponseEntity.ok(userResponse); + } + + // redundant? as friend can be found as any other user? + @GetMapping("{user_id}/friends/{friend_id}") + public ResponseEntity> getOneFriend(@PathVariable int userId, @PathVariable int friendId) { + User user = this.userRepository.findById(userId).orElse(null); + User friend = this.userRepository.findById(friendId).orElse(null); + if (user == null || friend == null) { + this.errorResponse.set("User or friend not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.userResponse.set(friend); + return ResponseEntity.ok(userResponse); + } + + @DeleteMapping("{user_id}/friends/{friend_id}") + public ResponseEntity> removeFriend(@PathVariable int userId, @PathVariable int friendId) { + User user = this.userRepository.findById(userId).orElse(null); + User friend = this.userRepository.findById(friendId).orElse(null); + if (user == null || friend == null) { + this.errorResponse.set("User or friend not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + user.getFriends().remove(friend); + this.userResponse.set(friend); return ResponseEntity.ok(userResponse); } } diff --git a/src/main/java/com/booleanuk/api/model/User.java b/src/main/java/com/booleanuk/api/model/User.java index 83d9936..501f0e1 100644 --- a/src/main/java/com/booleanuk/api/model/User.java +++ b/src/main/java/com/booleanuk/api/model/User.java @@ -1,6 +1,7 @@ package com.booleanuk.api.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIncludeProperties; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Getter; @@ -28,6 +29,10 @@ public class User { @JsonIgnoreProperties("user") private List posts; + @ManyToMany + @JsonIncludeProperties({"id", "username"}) + private List friends; + public User(String username, String email) { this.username = username; this.email = email; From 574ad330a67b5849a13cb5812350194fbfb7dc9c Mon Sep 17 00:00:00 2001 From: Frida Anselin Date: Mon, 3 Feb 2025 15:49:00 +0100 Subject: [PATCH 5/7] Add comments for posts --- .../api/controller/PostController.java | 59 +++++++++++++++++-- .../java/com/booleanuk/api/model/Comment.java | 38 ++++++++++++ .../java/com/booleanuk/api/model/Post.java | 4 ++ .../api/repository/CommentRepository.java | 7 +++ .../api/response/CommentListResponse.java | 8 +++ .../api/response/CommentResponse.java | 6 ++ 6 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/booleanuk/api/model/Comment.java create mode 100644 src/main/java/com/booleanuk/api/repository/CommentRepository.java create mode 100644 src/main/java/com/booleanuk/api/response/CommentListResponse.java create mode 100644 src/main/java/com/booleanuk/api/response/CommentResponse.java diff --git a/src/main/java/com/booleanuk/api/controller/PostController.java b/src/main/java/com/booleanuk/api/controller/PostController.java index e6e72d3..feaba6e 100644 --- a/src/main/java/com/booleanuk/api/controller/PostController.java +++ b/src/main/java/com/booleanuk/api/controller/PostController.java @@ -1,13 +1,12 @@ package com.booleanuk.api.controller; +import com.booleanuk.api.model.Comment; import com.booleanuk.api.model.Post; import com.booleanuk.api.model.User; +import com.booleanuk.api.repository.CommentRepository; import com.booleanuk.api.repository.PostRepository; import com.booleanuk.api.repository.UserRepository; -import com.booleanuk.api.response.ErrorResponse; -import com.booleanuk.api.response.PostListResponse; -import com.booleanuk.api.response.PostResponse; -import com.booleanuk.api.response.Response; +import com.booleanuk.api.response.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -24,9 +23,14 @@ public class PostController { @Autowired private UserRepository userRepository; + @Autowired + private CommentRepository commentRepository; + private ErrorResponse errorResponse = new ErrorResponse(); private PostResponse postResponse = new PostResponse(); private PostListResponse postListResponse = new PostListResponse(); + private CommentListResponse commentListResponse = new CommentListResponse(); + private CommentResponse commentResponse = new CommentResponse(); @GetMapping public ResponseEntity> getAllPosts() { @@ -90,4 +94,51 @@ public ResponseEntity> deletePost(@PathVariable int id) { return ResponseEntity.ok(postResponse); } + @GetMapping("{id}/comments") + public ResponseEntity> getAllComments(@PathVariable int id) { + Post postWithComments = this.postRepository.findById(id).orElse(null); + if (postWithComments == null) { + errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.commentListResponse.set(postWithComments.getComments()); + return ResponseEntity.ok(commentListResponse); + } + + @PostMapping("{id}/comments") + public ResponseEntity> addComment(@PathVariable int id, @RequestBody Comment comment) { + Post postWithComments = this.postRepository.findById(id).orElse(null); + if (postWithComments == null) { + this.errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + postWithComments.getComments().add(comment); + this.commentResponse.set(comment); + return ResponseEntity.ok(commentResponse); + } + + @GetMapping("{post_id}/comments/{comment_id}") + public ResponseEntity> getOneComment(@PathVariable int postId, @PathVariable int commentId) { + Post post = this.postRepository.findById(postId).orElse(null); + Comment comment = this.commentRepository.findById(commentId).orElse(null); + if (post == null || comment == null) { + this.errorResponse.set("Post or comment not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.commentResponse.set(comment); + return ResponseEntity.ok(commentResponse); + } + + @DeleteMapping("{post_id}/friends/{comment_id}") + public ResponseEntity> removeComment(@PathVariable int postId, @PathVariable int commentId) { + Post post = this.postRepository.findById(postId).orElse(null); + Comment comment = this.commentRepository.findById(commentId).orElse(null); + if (post == null || comment == null) { + this.errorResponse.set("Post or comment not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + post.getComments().remove(comment); + this.commentResponse.set(comment); + return ResponseEntity.ok(commentResponse); + } } diff --git a/src/main/java/com/booleanuk/api/model/Comment.java b/src/main/java/com/booleanuk/api/model/Comment.java new file mode 100644 index 0000000..92cf30c --- /dev/null +++ b/src/main/java/com/booleanuk/api/model/Comment.java @@ -0,0 +1,38 @@ +package com.booleanuk.api.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "comments") +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @Column + private String content; + + @Column + @JsonFormat(pattern = "yyyy-MM-dd mm:ss") + private LocalDateTime posted; + + @ManyToOne + @JoinColumn(name = "post_id") + private Post post; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + public Comment(String content) { + this.content = content; + } +} diff --git a/src/main/java/com/booleanuk/api/model/Post.java b/src/main/java/com/booleanuk/api/model/Post.java index e181f88..f645085 100644 --- a/src/main/java/com/booleanuk/api/model/Post.java +++ b/src/main/java/com/booleanuk/api/model/Post.java @@ -8,6 +8,7 @@ import lombok.Setter; import java.time.LocalDateTime; +import java.util.List; @Getter @Setter @@ -34,6 +35,9 @@ public class Post { @JoinColumn(name = "user_id") private User user; + @OneToMany(mappedBy = "comment", cascade = CascadeType.REMOVE) + private List comments; + public Post(String title, String content) { this.title = title; this.content = content; diff --git a/src/main/java/com/booleanuk/api/repository/CommentRepository.java b/src/main/java/com/booleanuk/api/repository/CommentRepository.java new file mode 100644 index 0000000..b9d910c --- /dev/null +++ b/src/main/java/com/booleanuk/api/repository/CommentRepository.java @@ -0,0 +1,7 @@ +package com.booleanuk.api.repository; + +import com.booleanuk.api.model.Comment; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommentRepository extends JpaRepository { +} diff --git a/src/main/java/com/booleanuk/api/response/CommentListResponse.java b/src/main/java/com/booleanuk/api/response/CommentListResponse.java new file mode 100644 index 0000000..cf16b2c --- /dev/null +++ b/src/main/java/com/booleanuk/api/response/CommentListResponse.java @@ -0,0 +1,8 @@ +package com.booleanuk.api.response; + +import com.booleanuk.api.model.Comment; + +import java.util.List; + +public class CommentListResponse extends Response> { +} diff --git a/src/main/java/com/booleanuk/api/response/CommentResponse.java b/src/main/java/com/booleanuk/api/response/CommentResponse.java new file mode 100644 index 0000000..593d0dc --- /dev/null +++ b/src/main/java/com/booleanuk/api/response/CommentResponse.java @@ -0,0 +1,6 @@ +package com.booleanuk.api.response; + +import com.booleanuk.api.model.Comment; + +public class CommentResponse extends Response { +} From 12bc3b20c34ca1d814a62743f0e11f91da39296c Mon Sep 17 00:00:00 2001 From: Frida Anselin Date: Mon, 3 Feb 2025 16:43:47 +0100 Subject: [PATCH 6/7] Add reposts --- .../api/controller/PostController.java | 15 +++- .../api/controller/RepostController.java | 68 +++++++++++++++++++ .../java/com/booleanuk/api/model/Post.java | 3 + .../java/com/booleanuk/api/model/Repost.java | 34 ++++++++++ .../api/repository/RepostRepository.java | 7 ++ .../api/response/RepostListResponse.java | 8 +++ .../api/response/RepostResponse.java | 6 ++ 7 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/booleanuk/api/controller/RepostController.java create mode 100644 src/main/java/com/booleanuk/api/model/Repost.java create mode 100644 src/main/java/com/booleanuk/api/repository/RepostRepository.java create mode 100644 src/main/java/com/booleanuk/api/response/RepostListResponse.java create mode 100644 src/main/java/com/booleanuk/api/response/RepostResponse.java diff --git a/src/main/java/com/booleanuk/api/controller/PostController.java b/src/main/java/com/booleanuk/api/controller/PostController.java index feaba6e..c72c003 100644 --- a/src/main/java/com/booleanuk/api/controller/PostController.java +++ b/src/main/java/com/booleanuk/api/controller/PostController.java @@ -2,7 +2,6 @@ import com.booleanuk.api.model.Comment; import com.booleanuk.api.model.Post; -import com.booleanuk.api.model.User; import com.booleanuk.api.repository.CommentRepository; import com.booleanuk.api.repository.PostRepository; import com.booleanuk.api.repository.UserRepository; @@ -31,6 +30,7 @@ public class PostController { private PostListResponse postListResponse = new PostListResponse(); private CommentListResponse commentListResponse = new CommentListResponse(); private CommentResponse commentResponse = new CommentResponse(); + private RepostListResponse repostListResponse = new RepostListResponse(); @GetMapping public ResponseEntity> getAllPosts() { @@ -38,6 +38,17 @@ public ResponseEntity> getAllPosts() { return ResponseEntity.ok(postListResponse); } + @GetMapping("{id}/reposts") + public ResponseEntity> getAllRepostsForPost(@PathVariable int id) { + Post postWithReposts = this.postRepository.findById(id).orElse(null); + if (postWithReposts == null) { + this.errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.repostListResponse.set(postWithReposts.getReposts()); + return ResponseEntity.ok(repostListResponse); + } + @PostMapping public ResponseEntity> createPost(@RequestBody Post post) { post.setPosted(LocalDateTime.now()); @@ -86,7 +97,7 @@ public ResponseEntity> updatePost(@PathVariable int id, @RequestBody public ResponseEntity> deletePost(@PathVariable int id) { Post postToDelete = this.postRepository.findById(id).orElse(null); if (postToDelete == null) { - errorResponse.set("Not found"); + this.errorResponse.set("Not found"); return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); } this.postRepository.delete(postToDelete); diff --git a/src/main/java/com/booleanuk/api/controller/RepostController.java b/src/main/java/com/booleanuk/api/controller/RepostController.java new file mode 100644 index 0000000..36de212 --- /dev/null +++ b/src/main/java/com/booleanuk/api/controller/RepostController.java @@ -0,0 +1,68 @@ +package com.booleanuk.api.controller; + +import com.booleanuk.api.model.Post; +import com.booleanuk.api.model.Repost; +import com.booleanuk.api.repository.PostRepository; +import com.booleanuk.api.repository.RepostRepository; +import com.booleanuk.api.response.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDateTime; + +@RestController +@RequestMapping("reposts") +public class RepostController { + @Autowired + private RepostRepository repostRepository; + + @Autowired + private PostRepository postRepository; + + private ErrorResponse errorResponse = new ErrorResponse(); + private RepostResponse repostResponse = new RepostResponse(); + private RepostListResponse repostListResponse = new RepostListResponse(); + + @GetMapping + public ResponseEntity> getAllReposts() { + this.repostListResponse.set(this.repostRepository.findAll()); + return ResponseEntity.ok(repostListResponse); + } + + @PostMapping + public ResponseEntity> createRepost(@RequestBody Repost repost) { + repost.setReposted(LocalDateTime.now()); + try { + this.repostResponse.set(this.repostRepository.save(repost)); + } catch (Exception e) { + this.errorResponse.set("Bad request"); + return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + } + return new ResponseEntity<>(repostResponse, HttpStatus.CREATED); + } + + @GetMapping("{id}") + public ResponseEntity> getOneRepost(@PathVariable int id) { + Repost repost = this.repostRepository.findById(id).orElse(null); + if (repost == null) { + this.errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.repostResponse.set(repost); + return ResponseEntity.ok(repostResponse); + } + + @DeleteMapping("{id}") + public ResponseEntity> deleteRepost(@PathVariable int id) { + Repost repostToDelete = this.repostRepository.findById(id).orElse(null); + if (repostToDelete == null) { + this.errorResponse.set("Not found"); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + this.repostRepository.delete(repostToDelete); + this.repostResponse.set(repostToDelete); + return ResponseEntity.ok(repostResponse); + } +} diff --git a/src/main/java/com/booleanuk/api/model/Post.java b/src/main/java/com/booleanuk/api/model/Post.java index f645085..44dab1c 100644 --- a/src/main/java/com/booleanuk/api/model/Post.java +++ b/src/main/java/com/booleanuk/api/model/Post.java @@ -38,6 +38,9 @@ public class Post { @OneToMany(mappedBy = "comment", cascade = CascadeType.REMOVE) private List comments; + @OneToMany(mappedBy = "repost", cascade = CascadeType.REMOVE) + private List reposts; + public Post(String title, String content) { this.title = title; this.content = content; diff --git a/src/main/java/com/booleanuk/api/model/Repost.java b/src/main/java/com/booleanuk/api/model/Repost.java new file mode 100644 index 0000000..3b4737d --- /dev/null +++ b/src/main/java/com/booleanuk/api/model/Repost.java @@ -0,0 +1,34 @@ +package com.booleanuk.api.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "reposts") +public class Repost { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + @Column + @JsonFormat(pattern = "yyyy-MM-dd mm:ss") + private LocalDateTime reposted; + + @ManyToOne + @JoinColumn(name = "post_id") + private Post post; + + public Repost(int id) { + this.id = id; + } + +} diff --git a/src/main/java/com/booleanuk/api/repository/RepostRepository.java b/src/main/java/com/booleanuk/api/repository/RepostRepository.java new file mode 100644 index 0000000..945bcc4 --- /dev/null +++ b/src/main/java/com/booleanuk/api/repository/RepostRepository.java @@ -0,0 +1,7 @@ +package com.booleanuk.api.repository; + +import com.booleanuk.api.model.Repost; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RepostRepository extends JpaRepository { +} diff --git a/src/main/java/com/booleanuk/api/response/RepostListResponse.java b/src/main/java/com/booleanuk/api/response/RepostListResponse.java new file mode 100644 index 0000000..77e8bc2 --- /dev/null +++ b/src/main/java/com/booleanuk/api/response/RepostListResponse.java @@ -0,0 +1,8 @@ +package com.booleanuk.api.response; + +import com.booleanuk.api.model.Repost; + +import java.util.List; + +public class RepostListResponse extends Response> { +} diff --git a/src/main/java/com/booleanuk/api/response/RepostResponse.java b/src/main/java/com/booleanuk/api/response/RepostResponse.java new file mode 100644 index 0000000..2a54ff6 --- /dev/null +++ b/src/main/java/com/booleanuk/api/response/RepostResponse.java @@ -0,0 +1,6 @@ +package com.booleanuk.api.response; + +import com.booleanuk.api.model.Repost; + +public class RepostResponse extends Response { +} From 007751d5f54c9bb29b5ba2bc691832cf9f33d3da Mon Sep 17 00:00:00 2001 From: Frida Anselin Date: Mon, 3 Feb 2025 16:58:05 +0100 Subject: [PATCH 7/7] Refactor --- .../java/com/booleanuk/api/controller/UserController.java | 2 ++ src/main/java/com/booleanuk/api/model/Post.java | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/booleanuk/api/controller/UserController.java b/src/main/java/com/booleanuk/api/controller/UserController.java index 496340b..2608c36 100644 --- a/src/main/java/com/booleanuk/api/controller/UserController.java +++ b/src/main/java/com/booleanuk/api/controller/UserController.java @@ -27,6 +27,8 @@ public ResponseEntity> getAllUsers() { return ResponseEntity.ok(userListResponse); } + // get all posts from user + @PostMapping public ResponseEntity> createUser (@RequestBody User user) { try { diff --git a/src/main/java/com/booleanuk/api/model/Post.java b/src/main/java/com/booleanuk/api/model/Post.java index 44dab1c..d4cca30 100644 --- a/src/main/java/com/booleanuk/api/model/Post.java +++ b/src/main/java/com/booleanuk/api/model/Post.java @@ -35,10 +35,10 @@ public class Post { @JoinColumn(name = "user_id") private User user; - @OneToMany(mappedBy = "comment", cascade = CascadeType.REMOVE) + @OneToMany(cascade = CascadeType.REMOVE) private List comments; - @OneToMany(mappedBy = "repost", cascade = CascadeType.REMOVE) + @OneToMany(cascade = CascadeType.REMOVE) private List reposts; public Post(String title, String content) {