Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ out/

### VS Code ###
.vscode/

application.yml
java.docker.day.2-0.0.1-SNAPSHOT.jar
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM eclipse-temurin:18

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"]
11 changes: 11 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
}
}
21 changes: 21 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
services:
app:
image: 'music-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
environment:
- POSTGRES_USER=mypostgresuser
- POSTGRES_DATABASE=mypostgresuser
- POSTGRES_PASSWORD=mypostgrespassword
11 changes: 11 additions & 0 deletions src/main/java/com/booleanuk/api/Main.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
202 changes: 202 additions & 0 deletions src/main/java/com/booleanuk/api/controllers/UserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package com.booleanuk.api.controllers;

import com.booleanuk.api.models.BlogPost;
import com.booleanuk.api.models.Comment;
import com.booleanuk.api.models.User;
import com.booleanuk.api.repositories.BlogPostRepository;
import com.booleanuk.api.repositories.CommentRepository;
import com.booleanuk.api.repositories.UserRepository;
import com.booleanuk.api.responses.*;
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;

@Autowired
private BlogPostRepository blogPostRepository;

@Autowired
private CommentRepository commentRepository;

// Create user
@PostMapping
public ResponseEntity<UserResponse> create(@RequestBody User user) {
this.userRepository.save(user);
UserResponse response = new UserResponse();
response.set(user);
return new ResponseEntity<>(response, HttpStatus.CREATED);
}

// Get all users
@GetMapping
public ResponseEntity<UserListResponse> getAll() {
UserListResponse response = new UserListResponse();
response.set(this.userRepository.findAll());
return ResponseEntity.ok(response);
}

// Get specific user
@GetMapping("{id}")
public ResponseEntity<Response<?>> getSpecific(@PathVariable int id) {
User user = this.userRepository.findById(id).orElse(null);
if (user == null) {
ErrorResponse error = new ErrorResponse();
error.set("A user with this id was not found.");
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
UserResponse response = new UserResponse();
response.set(user);
return ResponseEntity.ok(response);
}

// Update user
@PutMapping("{id}")
public ResponseEntity<Response<?>> update(@PathVariable int id, @RequestBody User user) {
User originalUser = this.userRepository.findById(id).orElse(null);
if (originalUser == null) {
ErrorResponse error = new ErrorResponse();
error.set("Not found");
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
user.setId(id);
try {
this.userRepository.save(user);
} catch (Exception e) {
ErrorResponse error = new ErrorResponse();
error.set("Bad request");
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
UserResponse response = new UserResponse();
response.set(user);
return new ResponseEntity<>(response, HttpStatus.CREATED);
}

// Delete user
@DeleteMapping("{id}")
public ResponseEntity<Response<?>> delete(@PathVariable int id) {
User user = this.userRepository.findById(id).orElse(null);
if (user == null) {
ErrorResponse error = new ErrorResponse();
error.set("Not found");
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}

UserResponse response = new UserResponse();
response.set(user);

try {
this.userRepository.delete(user);
} catch (Exception e) {
ErrorResponse error = new ErrorResponse();
error.set("Bad request");
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
return ResponseEntity.ok(response);
}

// Post blogpost for specific user
@PostMapping("{userId}/blogposts")
public ResponseEntity<Response<?>> publishBlogPostForUser(
@PathVariable int userId,
@RequestBody BlogPost blogpost) {

try {
User user = this.userRepository.findById(userId).orElse(null);
if (user == null) {
ErrorResponse error = new ErrorResponse();
error.set("A user with this id was not found.");
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}

// Populate blogpost with publisher info and save
blogpost.setPublisher(user);
this.blogPostRepository.save(blogpost);

// Add blogpost to user and save user
user.getBlogPosts().add(blogpost);
this.userRepository.save(user);

BlogPostResponse response = new BlogPostResponse();
response.set(blogpost);
return new ResponseEntity<>(response, HttpStatus.CREATED);

} catch (Exception e) {
ErrorResponse error = new ErrorResponse();
error.set("An error occurred: " + e.getMessage());
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}

// Post comment on blogpost
// NOTE: simplified, could/should be done in other ways
@PostMapping("{userId}/blogposts/{blogpostId}/comment")
public ResponseEntity<Response<?>> publishCommentOnBlogPostForUser(
@PathVariable int userId,
@PathVariable int blogpostId,
@RequestBody Comment comment) {

try {
// Find corresponding user (publisher)
User user = this.userRepository.findById(userId).orElse(null);
if (user == null) {
ErrorResponse error = new ErrorResponse();
error.set("A user with this id was not found.");
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
comment.setPublisher(user);

// Find corresponding blogpost
BlogPost blogpost = this.blogPostRepository.findById(blogpostId).orElse(null);
if (blogpost == null) {
ErrorResponse error = new ErrorResponse();
error.set("A blogpost with this id was not found.");
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
blogpost.getComments().add(comment);
comment.setBlogPost(blogpost);

this.commentRepository.save(comment);

CommentResponse response = new CommentResponse();
response.set(comment);
return new ResponseEntity<>(response, HttpStatus.CREATED);

} catch (Exception e) {
ErrorResponse error = new ErrorResponse();
error.set("An error occurred: " + e.getMessage());
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}

@GetMapping("blogposts")
public ResponseEntity<BlogPostListResponse> getAllBlogposts() {
BlogPostListResponse response = new BlogPostListResponse();
response.set(this.blogPostRepository.findAll());
return ResponseEntity.ok(response);
}

// Get all comments for a specific blogpost
@GetMapping("blogposts/{blogpostId}/comments")
public ResponseEntity<Response<?>> getCommentOnBlogPostForUser(
@PathVariable int blogpostId) {

BlogPost blogpost = this.blogPostRepository.findById(blogpostId).orElse(null);
if (blogpost == null) {
ErrorResponse error = new ErrorResponse();
error.set("A blogpost with this id was not found.");
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}

BlogPostResponse response = new BlogPostResponse();
response.set(blogpost);
return ResponseEntity.ok(response);
}

}
43 changes: 43 additions & 0 deletions src/main/java/com/booleanuk/api/models/BlogPost.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.booleanuk.api.models;

import com.fasterxml.jackson.annotation.JsonBackReference;
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Data
@NoArgsConstructor
@Entity
@Table
public class BlogPost {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

@ManyToOne
@JoinColumn
@JsonBackReference // To avoid recursion error
private User publisher;

@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createdAt;

// Content text of blogpost
@Column
private String text;

@OneToMany
@JoinColumn
private List<Comment> comments = new ArrayList<>();

public BlogPost(String text) {
this.text = text;
}
}
48 changes: 48 additions & 0 deletions src/main/java/com/booleanuk/api/models/Comment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.booleanuk.api.models;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@Entity
@Table
public class Comment {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

@ManyToOne // Many comments can be published by one user
@JoinColumn
@JsonIgnoreProperties({"comment"})
private User publisher;

@ManyToOne // Many comments can belong to one blog post
@JoinColumn
@JsonBackReference // To avoid recursion error
private BlogPost blogPost;

@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createdAt;

@Column
private String text;

public Comment(User publisher, BlogPost blogPost, String text) {
this.publisher = publisher;
this.blogPost = blogPost;
this.text = text;
}

public Comment(String text) {
this.text = text;
}
}
Loading