Completed the api through the use of DTO's

This commit is contained in:
2026-01-03 23:45:08 +00:00
parent 1358b8981c
commit ee681ad1f3
17 changed files with 272 additions and 51 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ application.properties
.project
.classpath
.vscode/
.env

21
Dockerfile Normal file
View File

@@ -0,0 +1,21 @@
# -------- Build stage --------
FROM maven:3.9.6-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn -B dependency:go-offline
COPY src ./src
RUN mvn -B clean package -DskipTests
# -------- Runtime stage --------
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 9010
ENTRYPOINT ["java", "-jar", "app.jar"]

View File

@@ -7,12 +7,15 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.zaine.app.dto.CreateCommentDTO;
import org.zaine.app.model.Comments;
import org.zaine.app.service.CommentsService;
@RestController
@RequestMapping("/api/comments")
public class CommentsController {
private static final Logger logger = System.getLogger(CommentsController.class.getName());
@Autowired
@@ -25,14 +28,44 @@ public class CommentsController {
}
@GetMapping("/comments/item/{id}")
public Comments getCommentById(@PathVariable Long id) {
@GetMapping("/item/{id}")
public Comments getCommentById(@PathVariable Integer id) {
return commentsService.getCommentById(id);
}
@GetMapping("/comments/items")
@GetMapping("")
public List<Comments> getAllComments() {
return commentsService.getAllComments();
}
@PostMapping(
value = "",
consumes = "application/json"
)
public void addComment(@RequestBody CreateCommentDTO dto) {
if (dto.getContent() == null || dto.getContent().trim().isEmpty()) {
logger.log(System.Logger.Level.WARNING,
"Attempted to add empty comment. Operation aborted.");
return;
}
Comments newComment = new Comments();
newComment.setPageSlug(dto.getPageSlug());
newComment.setAuthor(dto.getAuthor());
newComment.setContent(dto.getContent());
newComment.setParentId(dto.getParentId());
newComment.setCreatedAt(java.time.Instant.now());
logger.log(System.Logger.Level.INFO,
"Adding comment to pageSlug: " + dto.getPageSlug());
commentsService.addComment(newComment);
}
@GetMapping("/thread/{comment_id}")
public List<Comments> getCommentThread(@PathVariable Integer comment_id) {
return commentsService.getCommentThread(comment_id);
}
}

View File

@@ -2,41 +2,48 @@ package org.zaine.app.controller;
import java.lang.System.Logger;
import java.util.List;
import java.util.Map;
import org.zaine.app.dto.CompetenciesDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.zaine.app.model.Competencies;
import org.zaine.app.service.CompetenciesService;
@RestController
@RequestMapping("/api/competencies")
public class CompetenciesController {
private static final Logger logger = System.getLogger(CompetenciesController.class.getName());
@Autowired
private CompetenciesService competenciesService;
@GetMapping("/competencies/items")
@GetMapping("/items")
public List<Competencies> getAllCompetencies() {
return competenciesService.getAllCompetencies();
}
@GetMapping("/competencies/item/{id}")
public Competencies getCompetencyById(@PathVariable Long id) {
@GetMapping("/item/{id}")
public Competencies getCompetencyById(@PathVariable Integer id) {
return competenciesService.getCompetencyById(id);
}
@PostMapping("/competencies/items/{id}/state")
public void updateCompetencyState(@PathVariable Long id, @RequestParam String newState) {
logger.log(System.Logger.Level.INFO, "Received request to update competency id " + id + " to new state: " + newState);
competenciesService.updateCompetencyState(id, newState);
@PostMapping(
value = "/items/{id}/state",
consumes = "application/json"
)
public void updateCompetencyState(
@PathVariable Integer id,
@RequestBody CompetenciesDTO request
) {
competenciesService.updateCompetencyState(id, request.getState());
}
}

View File

@@ -1,4 +1,4 @@
package org.zaine.app;
package org.zaine.app.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

View File

@@ -7,11 +7,13 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.zaine.app.dto.CreateNoteDTO;
import org.zaine.app.model.Notes;
import org.zaine.app.service.NotesService;
import java.util.List;
@RestController
@RequestMapping("/api")
public class NotesController {
@Autowired
private NotesService notesService;
@@ -22,12 +24,21 @@ public class NotesController {
return notesService.getAllNotes();
}
@PostMapping("/notes")
public Notes createNote(@RequestBody Notes note) {
@PostMapping(
value = "/notes",
consumes = "application/json"
)
public Notes createNote(@RequestBody CreateNoteDTO dto) {
Notes note = new Notes();
note.setAuthorName(dto.getAuthorName());
note.setContent(dto.getContent());
return notesService.createNote(note);
}
}

View File

@@ -0,0 +1,18 @@
package org.zaine.app.dto;
public class CompetenciesDTO {
private String state;
public CompetenciesDTO() {
// REQUIRED: default constructor
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}

View File

@@ -0,0 +1,50 @@
package org.zaine.app.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
public class CreateCommentDTO {
@JsonProperty("page_slug")
private String pageSlug;
private String author;
private String content;
@JsonProperty("parent_id")
private Integer parentId;
public CreateCommentDTO() {}
public String getPageSlug() {
return pageSlug;
}
public void setPageSlug(String pageSlug) {
this.pageSlug = pageSlug;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
}

View File

@@ -0,0 +1,29 @@
package org.zaine.app.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
public class CreateNoteDTO {
@JsonProperty("author_name")
private String authorName;
private String content;
public CreateNoteDTO() {}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}

View File

@@ -1,9 +1,13 @@
package org.zaine.app.model;
import java.time.Instant;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@@ -12,9 +16,9 @@ import jakarta.persistence.Table;
public class Comments {
@Id
@GeneratedValue
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
private Integer id;
@Column(name = "page_slug")
private String pageSlug;
@@ -25,17 +29,19 @@ public class Comments {
@Column(name="content")
private String content;
@JsonProperty("created_at")
@Column(name="created_at")
private Instant createdAt;
@JsonProperty("parent_id")
@Column(name="parent_id")
private Long parentId;
private Integer parentId;
public Long getId() {
public Integer getId() {
return id;
}
public void setId(Long id) {
public void setId(Integer id) {
this.id = id;
}
public String getPageSlug() {
@@ -62,10 +68,10 @@ public class Comments {
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
public Long getParentId() {
public Integer getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
public void setParentId(Integer parentId) {
this.parentId = parentId;
}

View File

@@ -6,15 +6,16 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
@Entity
@Table(name = "competencies")
public class Competencies {
@Id
@GeneratedValue
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Long id;
private Integer id;
@Column(name="title")
private String title;
@@ -29,7 +30,7 @@ public class Competencies {
private String group;
public Long getId() {
public Integer getId() {
return id;
}

View File

@@ -9,6 +9,8 @@ import jakarta.persistence.GeneratedValue;
import java.time.Instant;
import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonProperty;
@Entity
@Table(name = "public_notes")
public class Notes {
@@ -21,11 +23,14 @@ public class Notes {
@Column(name="content")
private String content;
@JsonProperty("author_name")
@Column(name="author_name")
private String authorName;
private String authorName;
@JsonProperty("created_at")
@Column(name="created_at")
private Instant createdAt;
private Instant createdAt;
public UUID getId() {
return id;
@@ -51,5 +56,9 @@ public class Notes {
return createdAt;
}
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
}

View File

@@ -1,8 +1,29 @@
package org.zaine.app.repositories;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.zaine.app.model.Comments;
public interface CommentsRepository extends JpaRepository<Comments, Long> {
public interface CommentsRepository extends JpaRepository<Comments, Integer> {
@Query(value = """
WITH RECURSIVE comment_tree AS (
SELECT id, parent_id, author, content, created_at, page_slug
FROM comments
WHERE id = :commentId
AND is_deleted = false
UNION ALL
SELECT c.id, c.parent_id, c.author, c.content, c.created_at, c.page_slug
FROM comments c
JOIN comment_tree ct ON c.parent_id = ct.id
WHERE c.is_deleted = false
)
SELECT * FROM comment_tree
ORDER BY created_at ASC
""", nativeQuery = true)
List<Comments> findCommentThread(@Param("commentId") Integer commentId);
}

View File

@@ -4,6 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.zaine.app.model.Competencies;
public interface CompetenciesRepository extends JpaRepository<Competencies, Long> {
public interface CompetenciesRepository extends JpaRepository<Competencies, Integer> {
}

View File

@@ -30,9 +30,25 @@ public class CommentsService {
return filteredComments;
}
public Comments getCommentById(Long id) {
public Comments getCommentById(Integer id) {
logger.log(System.Logger.Level.INFO, "Entering getCommentById with id: " + id);
return commentsRepository.findById(id).get();
}
public void addComment(Comments comment) {
if (comment == null) {
logger.log(System.Logger.Level.WARNING, "Comment is null. Operation aborted.");
return;
} else {
logger.log(System.Logger.Level.INFO, "Adding comment with id: " + comment.getId());
commentsRepository.save(comment);
logger.log(System.Logger.Level.INFO, "Added new comment with id: " + comment.getId());
}
}
public List<Comments> getCommentThread(Integer comment_id) {
logger.log(System.Logger.Level.INFO, "Entering getCommentThread with id: " + comment_id);
return commentsRepository.findCommentThread(comment_id);
}
}

View File

@@ -26,7 +26,7 @@ public class CompetenciesService {
}
public Competencies getCompetencyById(Long id) {
public Competencies getCompetencyById(Integer id) {
logger.log(System.Logger.Level.INFO, "Entering getCompetencyById with id: " + id);
@@ -34,21 +34,19 @@ public class CompetenciesService {
}
public void updateCompetencyState(Long id, String newState) {
Competencies competency = getCompetencyById(id);
public void updateCompetencyState(Integer id, String newState) {
if (!ALLOWED_STATES.contains(newState)) {
logger.log(System.Logger.Level.WARNING, "Invalid state: " + newState);
throw new IllegalArgumentException("Invalid state: " + newState);
}
if (newState == null) {
throw new IllegalArgumentException("State must not be null");
}
if (competency != null) {
competency.setState(newState);
if (!ALLOWED_STATES.contains(newState)) {
throw new IllegalArgumentException("Invalid state: " + newState);
}
Competencies competency = getCompetencyById(id);
competency.setState(newState);
competenciesRepository.save(competency);
logger.log(System.Logger.Level.INFO, "Updated competency id " + id + " to new state: " + newState);
} else {
logger.log(System.Logger.Level.WARNING, "Competency with id " + id + " not found.");
}
}
}

View File

@@ -40,7 +40,7 @@ public class CompetenciesServiceTest {
@Test
public void testGetCompetencyByIdReturnsCompetency() {
Competencies comp = new Competencies();
@NotNull Long id = comp.getId();
@NotNull Integer id = comp.getId();
when(competenciesRepository.findById(id)).thenReturn(Optional.of(comp));