Skip to content

Commit a173d30

Browse files
committed
add basic endpoints for channels
1 parent 54dfbeb commit a173d30

6 files changed

Lines changed: 226 additions & 17 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
package tech.artcoded.websitev2.changelogs;
3+
4+
import io.mongock.api.annotations.ChangeUnit;
5+
import io.mongock.api.annotations.Execution;
6+
import io.mongock.api.annotations.RollbackExecution;
7+
import lombok.extern.slf4j.Slf4j;
8+
import tech.artcoded.websitev2.pages.report.PostRepository;
9+
import tech.artcoded.websitev2.pages.report.ChannelService;
10+
11+
import java.io.IOException;
12+
13+
@ChangeUnit(id = "add-channel-to-post", order = "59", author = "Nordine Bittich")
14+
@Slf4j
15+
public class CHANGE_LOG_59_AddChannelToExistingPost {
16+
17+
@RollbackExecution
18+
public void rollbackExecution() {
19+
}
20+
21+
@Execution
22+
public void execute(PostRepository postRepository, ChannelService channelService) throws IOException {
23+
24+
postRepository.findAll().stream().map(f -> f.toBuilder()
25+
.channelId(channelService.createChannel(f.getId()).getId())
26+
.bookmarked(false).build()).forEach(postRepository::save);
27+
28+
}
29+
30+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
package tech.artcoded.websitev2.pages.report;
3+
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
9+
import tech.artcoded.websitev2.utils.helper.IdGenerators;
10+
import org.springframework.data.annotation.Id;
11+
import org.springframework.data.mongodb.core.mapping.Document;
12+
13+
import java.util.Date;
14+
import java.util.List;
15+
import java.util.ArrayList;
16+
17+
@Data
18+
@NoArgsConstructor
19+
@AllArgsConstructor
20+
@Builder(toBuilder = true)
21+
@Document
22+
public class Channel {
23+
@Id
24+
@Builder.Default
25+
private String id = IdGenerators.get();
26+
27+
@Builder.Default
28+
private List<String> subscribers = new ArrayList<>();
29+
30+
@Builder.Default
31+
private List<Message> messages = new ArrayList<>();
32+
33+
@Builder.Default
34+
private Date creationDate = new Date();
35+
36+
private String correlationId;
37+
38+
private Date updatedDate;
39+
40+
public record Message(String id, Date creationDate, String emailFrom, String content, List<String> attachmentIds,
41+
boolean read) {
42+
}
43+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
package tech.artcoded.websitev2.pages.report;
3+
4+
import org.springframework.data.mongodb.repository.MongoRepository;
5+
6+
import java.util.Date;
7+
import java.util.List;
8+
import java.util.Optional;
9+
10+
public interface ChannelRepository extends MongoRepository<Channel, String> {
11+
12+
Optional<Channel> findByCorrelationId(String correlationId);
13+
14+
List<Channel> findByCreationDateAfter(Date date);
15+
16+
List<Channel> findByCreationDateBefore(Date date);
17+
18+
List<Channel> findBySubscribersContaining(String email);
19+
20+
List<Channel> findByMessagesEmailFrom(String emailFrom);
21+
22+
List<Channel> findByMessagesContentContainingIgnoreCase(String keyword);
23+
24+
List<Channel> findByMessagesAttachmentIds(String attachmentId);
25+
26+
long countBySubscribersContaining(String email);
27+
28+
void deleteByCreationDateBefore(Date date);
29+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package tech.artcoded.websitev2.pages.report;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.data.mongodb.core.MongoTemplate;
5+
import org.springframework.data.mongodb.core.query.Criteria;
6+
import org.springframework.data.mongodb.core.query.Query;
7+
import org.springframework.data.mongodb.core.query.Update;
8+
import org.springframework.stereotype.Service;
9+
10+
import java.util.Date;
11+
import java.util.List;
12+
import java.util.Optional;
13+
14+
@Service
15+
@RequiredArgsConstructor
16+
public class ChannelService {
17+
18+
private final ChannelRepository channelRepository;
19+
private final MongoTemplate mongoTemplate;
20+
21+
public Channel createChannel(String correlationId) {
22+
return channelRepository.save(Channel.builder().correlationId(correlationId).build());
23+
}
24+
25+
public Optional<Channel> getChannel(String id) {
26+
return channelRepository.findById(id);
27+
}
28+
29+
public Optional<Channel> getChannelByCorrelationId(String id) {
30+
return channelRepository.findByCorrelationId(id);
31+
}
32+
33+
public Optional<Channel> updateChannel(Channel updatedChannel) {
34+
return channelRepository.findById(updatedChannel.getId())
35+
.map(existing -> existing.toBuilder().updatedDate(new Date())
36+
.messages(updatedChannel.getMessages())
37+
.subscribers(updatedChannel.getSubscribers()).build())
38+
.map(channelRepository::save);
39+
40+
}
41+
42+
public void addMessage(String channelId, Channel.Message message) {
43+
Query query = new Query(Criteria.where("_id").is(channelId));
44+
Update update = new Update().push("messages", message)
45+
.set("updatedDate", new Date());
46+
mongoTemplate.updateFirst(query, update, Channel.class);
47+
}
48+
49+
public void deleteMessage(String channelId, String messageId) {
50+
Query query = new Query(Criteria.where("_id").is(channelId));
51+
Update update = new Update().pull("messages", Query.query(Criteria.where("id").is(messageId)))
52+
.set("updatedDate", new Date());
53+
mongoTemplate.updateFirst(query, update, Channel.class);
54+
}
55+
56+
public void updateCorrelationId(String channelId, String correlationId) {
57+
Query query = new Query(Criteria.where("_id").is(channelId));
58+
Update update = new Update().set("correlationId", correlationId)
59+
.set("updatedDate", new Date());
60+
mongoTemplate.updateFirst(query, update, Channel.class);
61+
}
62+
63+
public void deleteChannel(String channelId) {
64+
channelRepository.deleteById(channelId);
65+
}
66+
67+
public List<Channel> findChannelsBySubscriber(String email) {
68+
return channelRepository.findBySubscribersContaining(email);
69+
}
70+
}

artcoded/src/main/java/tech/artcoded/websitev2/pages/report/Post.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public class Post {
5555
@Builder.Default
5656
private Set<String> tags = Set.of();
5757

58+
private String channelId;
59+
5860
public enum PostStatus {
5961
DRAFT, IN_PROGRESS, PENDING, DONE, CANCELLED
6062
}

artcoded/src/main/java/tech/artcoded/websitev2/pages/report/ReportController.java

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package tech.artcoded.websitev2.pages.report;
22

3+
import lombok.RequiredArgsConstructor;
34
import lombok.extern.slf4j.Slf4j;
45
import org.apache.commons.lang3.StringUtils;
56
import org.commonmark.node.Node;
@@ -28,41 +29,35 @@
2829
import tech.artcoded.websitev2.utils.func.CheckedFunction;
2930
import tech.artcoded.websitev2.utils.helper.IdGenerators;
3031

31-
import javax.inject.Inject;
32-
3332
import java.security.Principal;
3433
import java.util.*;
3534
import java.util.stream.Collectors;
35+
import java.util.stream.Stream;
3636

3737
@RestController
3838
@RequestMapping("/api/report")
39+
@RequiredArgsConstructor
3940
@Slf4j
4041
public class ReportController {
4142
private final MongoTemplate mongoTemplate;
4243

4344
private final PostRepository repository;
45+
private final ChannelService channelService;
4446
private final PostService postService;
4547
private final PostTagRepository postTagRepository;
4648
private final IFileUploadService fileUploadService;
4749

48-
@Inject
49-
public ReportController(MongoTemplate mongoTemplate, PostRepository repository, PostTagRepository postTagRepository,
50-
IFileUploadService fileUploadService, PostService postService) {
51-
this.mongoTemplate = mongoTemplate;
52-
this.repository = repository;
53-
this.postService = postService;
54-
this.postTagRepository = postTagRepository;
55-
this.fileUploadService = fileUploadService;
56-
}
57-
5850
@PostMapping("/new-post")
5951
public Post newPost(Principal principal) {
6052
var user = User.fromPrincipal(principal);
61-
return repository
62-
.save(Post.builder().status(PostStatus.IN_PROGRESS)
63-
.priority(Priority.MEDIUM)
64-
.author(user.getEmail())
65-
.title("Draft").content("Content here").build());
53+
var id = IdGenerators.get().replace("-", "");
54+
var post = Post.builder().status(PostStatus.IN_PROGRESS)
55+
.id(IdGenerators.get())
56+
.priority(Priority.MEDIUM)
57+
.author(user.getEmail())
58+
.channelId(channelService.createChannel(id).getId())
59+
.title("Draft").content("Content here").build();
60+
return repository.save(post);
6661
}
6762

6863
public record PostIts(Set<PostIt> todos, Set<PostIt> inProgress, Set<PostIt> done) {
@@ -163,6 +158,46 @@ public ResponseEntity<Post> getPostById(@RequestParam("id") String id) {
163158
return this.repository.findById(id).map(ResponseEntity::ok).orElseGet(ResponseEntity.noContent()::build);
164159
}
165160

161+
@PostMapping("/channel/subscribe")
162+
public ResponseEntity<Channel> getPostById(@RequestParam("id") String id, Principal principal) {
163+
var user = User.fromPrincipal(principal);
164+
return this.channelService.getChannelByCorrelationId(id)
165+
.map(ch -> ch.toBuilder()
166+
.subscribers(
167+
Stream.concat(ch.getSubscribers().stream(), Stream.of(user.getEmail())).distinct().toList())
168+
.build())
169+
.flatMap(ch -> channelService.updateChannel(ch))
170+
.map(ResponseEntity::ok).orElseGet(ResponseEntity.noContent()::build);
171+
}
172+
173+
@PostMapping("/channel/post")
174+
public void postMessage(@RequestParam("id") String id,
175+
@RequestParam("message") String message,
176+
@RequestPart("files") MultipartFile[] attachments,
177+
Principal principal) {
178+
var user = User.fromPrincipal(principal);
179+
this.channelService.getChannelByCorrelationId(id)
180+
.ifPresent(ch -> {
181+
var uploadIds = fileUploadService.uploadAll(Arrays.asList(attachments), ch.getId(), false);
182+
var msg = new Channel.Message(IdGenerators.get(), new Date(), user.getEmail(), message, uploadIds, false);
183+
channelService.addMessage(ch.getId(), msg);
184+
});
185+
}
186+
187+
@DeleteMapping("/channel/message")
188+
public ResponseEntity<Void> deleteMessage(@RequestParam("id") String id,
189+
@RequestParam("messageId") String messageId,
190+
Principal principal) {
191+
var user = User.fromPrincipal(principal);
192+
var ch = this.channelService.getChannelByCorrelationId(id)
193+
.orElseThrow(() -> new RuntimeException("channel not found"));
194+
if (ch.getMessages().stream().noneMatch(m -> m.id().equals(id) && m.emailFrom().equals(user.getEmail()))) {
195+
return ResponseEntity.badRequest().build();
196+
}
197+
channelService.deleteMessage(ch.getId(), messageId);
198+
return ResponseEntity.ok().build();
199+
}
200+
166201
@GetMapping("/latest")
167202
public ResponseEntity<Page<Post>> getLatest() {
168203
var pageable = PageRequest.of(0, 3);

0 commit comments

Comments
 (0)