Initial version that creates and gets notifications

This commit is contained in:
Shrikant Sharat Kandula 2021-04-29 16:11:40 +05:30
parent a0d2e8533d
commit 914eb6f0c2
12 changed files with 204 additions and 1 deletions

View File

@ -29,4 +29,5 @@ public interface Url {
String MARKETPLACE_ITEM_URL = BASE_URL + VERSION + "/items";
String ASSET_URL = BASE_URL + VERSION + "/assets";
String COMMENT_URL = BASE_URL + VERSION + "/comments";
String NOTIFICATION_URL = BASE_URL + VERSION + "/notifications";
}

View File

@ -0,0 +1,21 @@
package com.appsmith.server.controllers;
import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Notification;
import com.appsmith.server.services.NotificationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping(Url.NOTIFICATION_URL)
public class NotificationController extends BaseController<NotificationService, Notification, String> {
@Autowired
public NotificationController(NotificationService service) {
super(service);
}
}

View File

@ -32,6 +32,12 @@ public class Comment extends BaseDomain {
Body body;
/**
* Indicates whether this comment is the leading comment in it's thread. Such a comment cannot be deleted.
*/
@JsonIgnore
Boolean leading;
@Data
public static class Body {
List<Block> blocks;

View File

@ -0,0 +1,12 @@
package com.appsmith.server.domains;
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
@Data
public class CommentNotification extends Notification {
Comment comment;
}

View File

@ -0,0 +1,24 @@
package com.appsmith.server.domains;
import com.appsmith.external.models.BaseDomain;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@EqualsAndHashCode(callSuper = true)
@Document
public class Notification extends BaseDomain {
// TODO: This class extends BaseDomain, so it has policies. Should we use information from policies instead of this field?
String forUsername;
/**
* Read status for this notification. If it is `true`, then this notification is read. If `false` or `null`, it's unread.
*/
Boolean isRead;
public void cleanForClient() {
}
}

View File

@ -13,11 +13,13 @@ import com.appsmith.server.repositories.ApplicationRepository;
import com.appsmith.server.repositories.DatasourceRepository;
import com.appsmith.server.repositories.NewActionRepository;
import com.appsmith.server.repositories.NewPageRepository;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -282,4 +284,18 @@ public class PolicyUtils {
return false;
}
public Set<String> findUsernamesWithPermission(Set<Policy> policies, AclPermission permission) {
if (CollectionUtils.isNotEmpty(policies) && permission != null) {
final String permissionString = permission.getValue();
for (Policy policy : policies) {
if (permissionString.equals(policy.getPermission())) {
return policy.getUsers();
}
}
}
return Collections.emptySet();
}
}

View File

@ -0,0 +1,6 @@
package com.appsmith.server.repositories;
import com.appsmith.server.domains.Notification;
public interface CustomNotificationRepository extends AppsmithRepository<Notification> {
}

View File

@ -0,0 +1,13 @@
package com.appsmith.server.repositories;
import com.appsmith.server.domains.Notification;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.convert.MongoConverter;
public class CustomNotificationRepositoryImpl extends BaseAppsmithRepositoryImpl<Notification> implements CustomNotificationRepository {
public CustomNotificationRepositoryImpl(ReactiveMongoOperations mongoOperations, MongoConverter mongoConverter) {
super(mongoOperations, mongoConverter);
}
}

View File

@ -0,0 +1,12 @@
package com.appsmith.server.repositories;
import com.appsmith.server.domains.Notification;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
@Repository
public interface NotificationRepository extends BaseRepository<Notification, String>, CustomNotificationRepository {
Flux<Notification> findByForUsername(String userId);
}

View File

@ -5,10 +5,13 @@ import com.appsmith.server.acl.PolicyGenerator;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Comment;
import com.appsmith.server.domains.CommentNotification;
import com.appsmith.server.domains.CommentThread;
import com.appsmith.server.domains.Notification;
import com.appsmith.server.domains.User;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.PolicyUtils;
import com.appsmith.server.repositories.CommentRepository;
import com.appsmith.server.repositories.CommentThreadRepository;
import lombok.extern.slf4j.Slf4j;
@ -41,8 +44,10 @@ public class CommentServiceImpl extends BaseService<CommentRepository, Comment,
private final SessionUserService sessionUserService;
private final ApplicationService applicationService;
private final NotificationService notificationService;
private final PolicyGenerator policyGenerator;
private final PolicyUtils policyUtils;
public CommentServiceImpl(
Scheduler scheduler,
@ -54,13 +59,17 @@ public class CommentServiceImpl extends BaseService<CommentRepository, Comment,
CommentThreadRepository threadRepository,
SessionUserService sessionUserService,
ApplicationService applicationService,
PolicyGenerator policyGenerator
NotificationService notificationService,
PolicyGenerator policyGenerator,
PolicyUtils policyUtils
) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
this.threadRepository = threadRepository;
this.sessionUserService = sessionUserService;
this.applicationService = applicationService;
this.notificationService = notificationService;
this.policyGenerator = policyGenerator;
this.policyUtils = policyUtils;
}
@Override
@ -90,6 +99,20 @@ public class CommentServiceImpl extends BaseService<CommentRepository, Comment,
String authorName = user.getName() != null ? user.getName(): user.getUsername();
comment1.setAuthorName(authorName);
return repository.save(comment1);
})
.flatMap(savedComment -> {
final Set<String> usernames = policyUtils.findUsernamesWithPermission(
savedComment.getPolicies(), AclPermission.READ_COMMENT);
List<Mono<Notification>> monos = new ArrayList<>();
for (String username : usernames) {
final CommentNotification notification = new CommentNotification();
notification.setComment(savedComment);
notification.setForUsername(username);
monos.add(notificationService.create(notification));
}
return Flux.concat(monos).then(Mono.just(savedComment));
});
}
@ -137,6 +160,7 @@ public class CommentServiceImpl extends BaseService<CommentRepository, Comment,
List<Mono<Comment>> commentSaverMonos = new ArrayList<>();
if (!CollectionUtils.isEmpty(thread.getComments())) {
thread.getComments().get(0).setLeading(true);
for (final Comment comment : thread.getComments()) {
comment.setId(null);
commentSaverMonos.add(create(thread.getId(), comment));

View File

@ -0,0 +1,7 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.Notification;
public interface NotificationService extends CrudService<Notification, String> {
}

View File

@ -0,0 +1,61 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.Notification;
import com.appsmith.server.repositories.NotificationRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import javax.validation.Validator;
@Slf4j
@Service
public class NotificationServiceImpl
extends BaseService<NotificationRepository, Notification, String>
implements NotificationService {
private final SessionUserService sessionUserService;
public NotificationServiceImpl(
Scheduler scheduler,
Validator validator,
MongoConverter mongoConverter,
ReactiveMongoTemplate reactiveMongoTemplate,
NotificationRepository repository,
AnalyticsService analyticsService,
SessionUserService sessionUserService
) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
this.sessionUserService = sessionUserService;
}
@Override
public Mono<Notification> create(Notification notification) {
Mono<Notification> notificationWithUsernameMono;
if (StringUtils.isEmpty(notification.getForUsername())) {
notificationWithUsernameMono = sessionUserService.getCurrentUser()
.map(user -> {
notification.setForUsername(user.getUsername());
return notification;
});
} else {
notificationWithUsernameMono = Mono.just(notification);
}
return notificationWithUsernameMono
.flatMap(super::create);
}
@Override
public Flux<Notification> get(MultiValueMap<String, String> params) {
return sessionUserService.getCurrentUser()
.flatMapMany(user -> repository.findByForUsername(user.getUsername()));
}
}