From f00b917f7a77c136bd719d6463342f580425ddab Mon Sep 17 00:00:00 2001 From: mochizuki <1p41p4jejo@gmail.com> Date: Sun, 12 Jan 2025 12:18:47 +0800 Subject: [PATCH 1/5] feat: add node entity and repository --- .../service/timeline/entity/NodeEntity.java | 41 +++++++++++++++++++ .../timeline/entity/NodeRepository.java | 14 +++++++ 2 files changed, 55 insertions(+) create mode 100644 src/main/java/tw/commonground/backend/service/timeline/entity/NodeEntity.java create mode 100644 src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java diff --git a/src/main/java/tw/commonground/backend/service/timeline/entity/NodeEntity.java b/src/main/java/tw/commonground/backend/service/timeline/entity/NodeEntity.java new file mode 100644 index 0000000..5517e45 --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/entity/NodeEntity.java @@ -0,0 +1,41 @@ +package tw.commonground.backend.service.timeline.entity; + +import jakarta.persistence.*; +import lombok.*; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import tw.commonground.backend.service.issue.entity.IssueEntity; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@Setter +@Entity +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EntityListeners(AuditingEntityListener.class) +public class NodeEntity { + @Id + @GeneratedValue + private UUID id; + + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; + + private String title; + + private String description; + + private LocalDate date; + + @ManyToOne + private IssueEntity issue; + +} diff --git a/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java b/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java new file mode 100644 index 0000000..aee9b4b --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java @@ -0,0 +1,14 @@ +package tw.commonground.backend.service.timeline.entity; + +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.JpaRepository; +import tw.commonground.backend.service.issue.entity.IssueEntity; + +import java.util.List; +import java.util.UUID; + +public interface NodeRepository extends JpaRepository { +// List findAllByIssue(IssueEntity issue, Sort sort); + + List findAllByIssueId(UUID issueId); +} From 3ac43fe8ae74304bcde92e80f59eae059ed9cf1c Mon Sep 17 00:00:00 2001 From: mochizuki <1p41p4jejo@gmail.com> Date: Sun, 12 Jan 2025 12:18:57 +0800 Subject: [PATCH 2/5] feat: add timeline dto --- .../service/timeline/dto/NodeMapper.java | 25 +++++++++++++++++++ .../service/timeline/dto/NodeRequest.java | 22 ++++++++++++++++ .../service/timeline/dto/NodeResponse.java | 25 +++++++++++++++++++ .../service/timeline/dto/TimelineMapper.java | 19 ++++++++++++++ .../timeline/dto/TimelineResponse.java | 14 +++++++++++ 5 files changed, 105 insertions(+) create mode 100644 src/main/java/tw/commonground/backend/service/timeline/dto/NodeMapper.java create mode 100644 src/main/java/tw/commonground/backend/service/timeline/dto/NodeRequest.java create mode 100644 src/main/java/tw/commonground/backend/service/timeline/dto/NodeResponse.java create mode 100644 src/main/java/tw/commonground/backend/service/timeline/dto/TimelineMapper.java create mode 100644 src/main/java/tw/commonground/backend/service/timeline/dto/TimelineResponse.java diff --git a/src/main/java/tw/commonground/backend/service/timeline/dto/NodeMapper.java b/src/main/java/tw/commonground/backend/service/timeline/dto/NodeMapper.java new file mode 100644 index 0000000..2c5a233 --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/dto/NodeMapper.java @@ -0,0 +1,25 @@ +package tw.commonground.backend.service.timeline.dto; + +import tw.commonground.backend.service.timeline.entity.NodeEntity; + +import java.time.format.DateTimeFormatter; + +public class NodeMapper { + + private static final DateTimeFormatter formatters = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + private NodeMapper() { + // hide the constructor + } + + public static NodeResponse toResponse(NodeEntity entity) { + return NodeResponse.builder() + .id(entity.getId().toString()) + .createdAt(entity.getCreatedAt()) + .updatedAt(entity.getUpdatedAt()) + .title(entity.getTitle()) + .description(entity.getDescription()) + .date(entity.getDate().format(formatters)) + .build(); + } +} diff --git a/src/main/java/tw/commonground/backend/service/timeline/dto/NodeRequest.java b/src/main/java/tw/commonground/backend/service/timeline/dto/NodeRequest.java new file mode 100644 index 0000000..8de87a1 --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/dto/NodeRequest.java @@ -0,0 +1,22 @@ +package tw.commonground.backend.service.timeline.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class NodeRequest { + + @NotBlank(message = "Title is required") + private String title; + + private String description; + + @NotBlank(message = "Date is required") + @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$", message = "Date must be in the format yyyy-MM-dd") + private String date; +} diff --git a/src/main/java/tw/commonground/backend/service/timeline/dto/NodeResponse.java b/src/main/java/tw/commonground/backend/service/timeline/dto/NodeResponse.java new file mode 100644 index 0000000..616de8c --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/dto/NodeResponse.java @@ -0,0 +1,25 @@ +package tw.commonground.backend.service.timeline.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@Builder +public class NodeResponse { + + private String id; + + private LocalDateTime createdAt; + + private LocalDateTime updatedAt; + + private String title; + + private String description; + + private String date; +} diff --git a/src/main/java/tw/commonground/backend/service/timeline/dto/TimelineMapper.java b/src/main/java/tw/commonground/backend/service/timeline/dto/TimelineMapper.java new file mode 100644 index 0000000..0df4d83 --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/dto/TimelineMapper.java @@ -0,0 +1,19 @@ +package tw.commonground.backend.service.timeline.dto; + +import tw.commonground.backend.service.timeline.entity.NodeEntity; + +import java.util.List; +import java.util.stream.Collectors; + +public class TimelineMapper { + private TimelineMapper() { + // hide the constructor + } + + public static TimelineResponse toResponse(List nodes) { + List content = nodes.stream() + .map(NodeMapper::toResponse) + .collect(Collectors.toList()); + return new TimelineResponse(content); + } +} diff --git a/src/main/java/tw/commonground/backend/service/timeline/dto/TimelineResponse.java b/src/main/java/tw/commonground/backend/service/timeline/dto/TimelineResponse.java new file mode 100644 index 0000000..51fd32e --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/dto/TimelineResponse.java @@ -0,0 +1,14 @@ +package tw.commonground.backend.service.timeline.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +public class TimelineResponse { + private List content; +} From 5ab1420b46937732f7713f803a56584d220f356f Mon Sep 17 00:00:00 2001 From: mochizuki <1p41p4jejo@gmail.com> Date: Sun, 12 Jan 2025 12:19:06 +0800 Subject: [PATCH 3/5] feat: add timeline controller and service --- .../service/timeline/TimelineController.java | 55 ++++++++++++ .../service/timeline/TimelineService.java | 83 +++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 src/main/java/tw/commonground/backend/service/timeline/TimelineController.java create mode 100644 src/main/java/tw/commonground/backend/service/timeline/TimelineService.java diff --git a/src/main/java/tw/commonground/backend/service/timeline/TimelineController.java b/src/main/java/tw/commonground/backend/service/timeline/TimelineController.java new file mode 100644 index 0000000..da058b9 --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/TimelineController.java @@ -0,0 +1,55 @@ +package tw.commonground.backend.service.timeline; + +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import tw.commonground.backend.service.timeline.dto.*; +import tw.commonground.backend.service.timeline.entity.NodeEntity; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/api") +public class TimelineController { + + private final TimelineService timelineService; + + public TimelineController(TimelineService timelineService) { + this.timelineService = timelineService; + } + + @GetMapping("/issue/{id}/timeline") + public ResponseEntity getTimeline(@PathVariable UUID id) { + List nodes = timelineService.getNodes(id); + TimelineResponse response = TimelineMapper.toResponse(nodes); + return ResponseEntity.ok(response); + } + + @PostMapping("/issue/{id}/timeline") + public ResponseEntity createNode(@PathVariable UUID id, @Valid @RequestBody NodeRequest request) { + NodeEntity node = timelineService.createNode(id, request); + NodeResponse response = NodeMapper.toResponse(node); + return ResponseEntity.ok(response); + } + + @GetMapping("/timeline/node/{nodeId}") + public ResponseEntity getNode(@PathVariable UUID nodeId) { + NodeEntity node = timelineService.getNode(nodeId); + NodeResponse response = NodeMapper.toResponse(node); + return ResponseEntity.ok(response); + } + + @PutMapping("/timeline/node/{nodeId}") + public ResponseEntity updateNode(@PathVariable UUID nodeId, @Valid @RequestBody NodeRequest request) { + NodeEntity node = timelineService.updateNode(nodeId, request); + NodeResponse response = NodeMapper.toResponse(node); + return ResponseEntity.ok(response); + } + + @DeleteMapping("/timeline/node/{nodeId}") + public ResponseEntity deleteNode(@PathVariable UUID nodeId) { + timelineService.deleteNode(nodeId); + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/tw/commonground/backend/service/timeline/TimelineService.java b/src/main/java/tw/commonground/backend/service/timeline/TimelineService.java new file mode 100644 index 0000000..69c1229 --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/TimelineService.java @@ -0,0 +1,83 @@ +package tw.commonground.backend.service.timeline; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.stereotype.Service; +import org.w3c.dom.Node; +import tw.commonground.backend.exception.EntityNotFoundException; +import tw.commonground.backend.exception.ValidationException; +import tw.commonground.backend.service.issue.IssueService; +import tw.commonground.backend.service.issue.entity.IssueEntity; +import tw.commonground.backend.service.timeline.dto.NodeRequest; +import tw.commonground.backend.service.timeline.entity.NodeEntity; +import tw.commonground.backend.service.timeline.entity.NodeRepository; + +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.List; +import java.util.UUID; + +@Service +public class TimelineService { + + private final IssueService issueService; + + private final NodeRepository nodeRepository; + + @PersistenceContext + private EntityManager entityManager; + + public TimelineService(IssueService issueService, NodeRepository nodeRepository) { + this.issueService = issueService; + this.nodeRepository = nodeRepository; + } + + public List getNodes(UUID issueId) { + issueService.throwIfIssueNotExist(issueId); + return nodeRepository.findAllByIssueId(issueId); + } + + public NodeEntity getNode(UUID nodeId) { + return nodeRepository.findById(nodeId) + .orElseThrow(() -> new EntityNotFoundException("Node", "id", nodeId.toString())); + } + + public NodeEntity createNode(UUID issueId, NodeRequest request) { + issueService.throwIfIssueNotExist(issueId); + + NodeEntity node = new NodeEntity(); + node.setTitle(request.getTitle()); + node.setDescription(request.getDescription()); + + try { + node.setDate(LocalDate.parse(request.getDate())); + } catch (DateTimeParseException e) { + throw new ValidationException("date", "Invalid date format"); + } + + IssueEntity issue = entityManager.getReference(IssueEntity.class, issueId); + node.setIssue(issue); + + return nodeRepository.save(node); + } + + public NodeEntity updateNode(UUID nodeId, NodeRequest request) { + NodeEntity node = nodeRepository.findById(nodeId) + .orElseThrow(() -> new EntityNotFoundException("Node", "id", nodeId.toString())); + + node.setTitle(request.getTitle()); + node.setDescription(request.getDescription()); + + try { + node.setDate(LocalDate.parse(request.getDate())); + } catch (DateTimeParseException e) { + throw new ValidationException("date", "Invalid date format"); + } + + return nodeRepository.save(node); + } + + public void deleteNode(UUID nodeId) { + nodeRepository.deleteById(nodeId); + } +} From 7221aee77d75865b6cdb9d19d0eea121a70e7239 Mon Sep 17 00:00:00 2001 From: mochizuki <1p41p4jejo@gmail.com> Date: Sun, 12 Jan 2025 12:30:11 +0800 Subject: [PATCH 4/5] style: fix coding style issues --- .../backend/service/timeline/TimelineService.java | 1 - .../backend/service/timeline/dto/NodeMapper.java | 6 +++--- .../backend/service/timeline/dto/TimelineMapper.java | 2 +- .../backend/service/timeline/dto/package-info.java | 1 + .../backend/service/timeline/entity/NodeRepository.java | 2 -- .../backend/service/timeline/entity/package-info.java | 1 + .../commonground/backend/service/timeline/package-info.java | 1 + 7 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 src/main/java/tw/commonground/backend/service/timeline/dto/package-info.java create mode 100644 src/main/java/tw/commonground/backend/service/timeline/entity/package-info.java create mode 100644 src/main/java/tw/commonground/backend/service/timeline/package-info.java diff --git a/src/main/java/tw/commonground/backend/service/timeline/TimelineService.java b/src/main/java/tw/commonground/backend/service/timeline/TimelineService.java index 69c1229..3b30c35 100644 --- a/src/main/java/tw/commonground/backend/service/timeline/TimelineService.java +++ b/src/main/java/tw/commonground/backend/service/timeline/TimelineService.java @@ -3,7 +3,6 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.springframework.stereotype.Service; -import org.w3c.dom.Node; import tw.commonground.backend.exception.EntityNotFoundException; import tw.commonground.backend.exception.ValidationException; import tw.commonground.backend.service.issue.IssueService; diff --git a/src/main/java/tw/commonground/backend/service/timeline/dto/NodeMapper.java b/src/main/java/tw/commonground/backend/service/timeline/dto/NodeMapper.java index 2c5a233..cbab8d4 100644 --- a/src/main/java/tw/commonground/backend/service/timeline/dto/NodeMapper.java +++ b/src/main/java/tw/commonground/backend/service/timeline/dto/NodeMapper.java @@ -4,9 +4,9 @@ import java.time.format.DateTimeFormatter; -public class NodeMapper { +public final class NodeMapper { - private static final DateTimeFormatter formatters = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private NodeMapper() { // hide the constructor @@ -19,7 +19,7 @@ public static NodeResponse toResponse(NodeEntity entity) { .updatedAt(entity.getUpdatedAt()) .title(entity.getTitle()) .description(entity.getDescription()) - .date(entity.getDate().format(formatters)) + .date(entity.getDate().format(FORMATTER)) .build(); } } diff --git a/src/main/java/tw/commonground/backend/service/timeline/dto/TimelineMapper.java b/src/main/java/tw/commonground/backend/service/timeline/dto/TimelineMapper.java index 0df4d83..616afb2 100644 --- a/src/main/java/tw/commonground/backend/service/timeline/dto/TimelineMapper.java +++ b/src/main/java/tw/commonground/backend/service/timeline/dto/TimelineMapper.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.stream.Collectors; -public class TimelineMapper { +public final class TimelineMapper { private TimelineMapper() { // hide the constructor } diff --git a/src/main/java/tw/commonground/backend/service/timeline/dto/package-info.java b/src/main/java/tw/commonground/backend/service/timeline/dto/package-info.java new file mode 100644 index 0000000..0bf0dfc --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/dto/package-info.java @@ -0,0 +1 @@ +package tw.commonground.backend.service.timeline.dto; diff --git a/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java b/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java index aee9b4b..279c571 100644 --- a/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java +++ b/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java @@ -1,8 +1,6 @@ package tw.commonground.backend.service.timeline.entity; -import org.springframework.data.domain.Sort; import org.springframework.data.jpa.repository.JpaRepository; -import tw.commonground.backend.service.issue.entity.IssueEntity; import java.util.List; import java.util.UUID; diff --git a/src/main/java/tw/commonground/backend/service/timeline/entity/package-info.java b/src/main/java/tw/commonground/backend/service/timeline/entity/package-info.java new file mode 100644 index 0000000..d4a7d29 --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/entity/package-info.java @@ -0,0 +1 @@ +package tw.commonground.backend.service.timeline.entity; diff --git a/src/main/java/tw/commonground/backend/service/timeline/package-info.java b/src/main/java/tw/commonground/backend/service/timeline/package-info.java new file mode 100644 index 0000000..cd30eb5 --- /dev/null +++ b/src/main/java/tw/commonground/backend/service/timeline/package-info.java @@ -0,0 +1 @@ +package tw.commonground.backend.service.timeline; From a74dc8e9976a0703a83902950a1f63ba158d023f Mon Sep 17 00:00:00 2001 From: mochizuki <1p41p4jejo@gmail.com> Date: Sun, 12 Jan 2025 12:30:40 +0800 Subject: [PATCH 5/5] chore: remove unnecessary method --- .../backend/service/timeline/entity/NodeRepository.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java b/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java index 279c571..ab40927 100644 --- a/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java +++ b/src/main/java/tw/commonground/backend/service/timeline/entity/NodeRepository.java @@ -6,7 +6,5 @@ import java.util.UUID; public interface NodeRepository extends JpaRepository { -// List findAllByIssue(IssueEntity issue, Sort sort); - List findAllByIssueId(UUID issueId); }