Skip to content

Commit

Permalink
Merge pull request #32 from elwyncrestha/feature/notification
Browse files Browse the repository at this point in the history
Feature: Integrated socket notification.
  • Loading branch information
elwyncrestha committed Jul 11, 2020
2 parents 5811691 + 2c282f2 commit 185f861
Show file tree
Hide file tree
Showing 15 changed files with 366 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package com.pemits.webcare.api.appointment.service;

import static com.pemits.webcare.core.constant.AppConstant.NOTIFY_NEW_APPOINTMENT;

import java.util.Map;

import org.springframework.stereotype.Service;

import com.pemits.webcare.api.appointment.entity.Appointment;
import com.pemits.webcare.api.appointment.repository.AppointmentRepository;
import com.pemits.webcare.api.appointment.repository.spec.AppointmentSpecBuilder;
import com.pemits.webcare.api.notification.component.NotificationComponent;
import com.pemits.webcare.api.notification.entity.Notification;
import com.pemits.webcare.api.patient.entity.Patient;
import com.pemits.webcare.api.patient.service.PatientService;
import com.pemits.webcare.api.user.entity.User;
import com.pemits.webcare.api.user.service.UserService;
import com.pemits.webcare.core.enums.NotificationStatus;
import com.pemits.webcare.core.enums.UserType;
import com.pemits.webcare.core.repository.BaseSpecBuilder;
import com.pemits.webcare.core.service.BaseServiceImpl;
Expand All @@ -26,16 +31,19 @@ public class AppointmentServiceImpl extends BaseServiceImpl<Appointment, Long> i
private final AppointmentRepository repository;
private final UserService userService;
private final PatientService patientService;
private final NotificationComponent notificationComponent;

public AppointmentServiceImpl(
AppointmentRepository repository,
UserService userService,
PatientService patientService) {
PatientService patientService,
NotificationComponent notificationComponent) {
super(repository);

this.repository = repository;
this.userService = userService;
this.patientService = patientService;
this.notificationComponent = notificationComponent;
}

@Override
Expand All @@ -51,7 +59,16 @@ public Appointment save(Appointment appointment) {
Patient savedPatient = patientService.save(newPatient);
appointment.setPatient(savedPatient);
}
return repository.save(appointment);
Appointment saved = repository.save(appointment);
// send socket notification
Notification notification = Notification.builder()
.status(NotificationStatus.UNSEEN)
.from(saved.getPatient().getUser())
.to(saved.getDoctor().getUser())
.message(NOTIFY_NEW_APPOINTMENT)
.build();
notificationComponent.sendAndSaveMessage(notification);
return saved;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.pemits.webcare.api.notification.component;

import static com.pemits.webcare.core.constant.AppConstant.SOCKET_SUBSCRIBER;

import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;

import com.pemits.webcare.api.notification.entity.Notification;
import com.pemits.webcare.api.notification.service.NotificationService;
import com.pemits.webcare.api.user.service.UserService;

/**
* @author Elvin Shrestha on 7/11/2020
*/
@Component
public class NotificationComponent {

private final SimpMessagingTemplate simpMessagingTemplate;
private final NotificationService notificationService;
private final UserService userService;

public NotificationComponent(
SimpMessagingTemplate simpMessagingTemplate,
NotificationService notificationService,
UserService userService
) {
this.simpMessagingTemplate = simpMessagingTemplate;
this.notificationService = notificationService;
this.userService = userService;
}

public Notification sendAndSaveMessage(Notification notification) {
Long toId = notification.getTo().getId();
String destination = String.format("%s/%s", SOCKET_SUBSCRIBER, toId);
Notification saved = notificationService.save(notification);
simpMessagingTemplate.convertAndSend(destination, notification);
return saved;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.pemits.webcare.api.notification.entity;

import javax.persistence.Entity;
import javax.persistence.OneToOne;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import com.pemits.webcare.api.user.entity.User;
import com.pemits.webcare.core.entity.BaseEntity;
import com.pemits.webcare.core.enums.NotificationStatus;

/**
* @author Elvin Shrestha on 7/11/2020
*/
@Entity
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Notification extends BaseEntity<Long> {

@OneToOne
private User from;

@OneToOne
private User to;

private String message;

private NotificationStatus status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.pemits.webcare.api.notification.repository;

import org.springframework.stereotype.Repository;

import com.pemits.webcare.api.notification.entity.Notification;
import com.pemits.webcare.core.repository.BaseRepository;

/**
* @author Elvin Shrestha on 7/11/2020
*/
@Repository
public interface NotificationRepository extends BaseRepository<Notification, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.pemits.webcare.api.notification.repository.spec;


import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.data.jpa.domain.Specification;

import com.pemits.webcare.api.notification.entity.Notification;
import com.pemits.webcare.core.enums.NotificationStatus;

/**
* @author Elvin Shrestha on 7/11/2020
*/
public class NotificationSpec implements Specification<Notification> {

public static final String FILTER_BY_TO_ID = "to";
public static final String FILTER_BY_STATUS = "status";

private final String property;
private final String value;

public NotificationSpec(String property, String value) {
this.property = property;
this.value = value;
}

@Override
public Predicate toPredicate(Root<Notification> root, CriteriaQuery<?> criteriaQuery,
CriteriaBuilder criteriaBuilder) {
switch (property) {
case FILTER_BY_TO_ID:
return criteriaBuilder
.equal(root.get(FILTER_BY_TO_ID).get("id"), Long.valueOf(value));
case FILTER_BY_STATUS:
return criteriaBuilder
.equal(root.get(FILTER_BY_STATUS), NotificationStatus.valueOf(value));
default:
return null;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.pemits.webcare.api.notification.repository.spec;

import java.util.Map;

import org.springframework.data.jpa.domain.Specification;

import com.pemits.webcare.api.notification.entity.Notification;
import com.pemits.webcare.core.repository.BaseSpecBuilder;

/**
* @author Elvin Shrestha on 7/11/2020
*/
public class NotificationSpecBuilder extends BaseSpecBuilder<Notification> {

public NotificationSpecBuilder(Map<String, String> params) {
super(params);
}

@Override
protected Specification<Notification> getSpecification(String property, String filterValue) {
return new NotificationSpec(property, filterValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.pemits.webcare.api.notification.service;

import com.pemits.webcare.api.notification.entity.Notification;
import com.pemits.webcare.core.service.BaseService;

/**
* @author Elvin Shrestha on 7/11/2020
*/
public interface NotificationService extends BaseService<Notification, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.pemits.webcare.api.notification.service;

import java.util.Map;

import org.springframework.stereotype.Service;

import com.pemits.webcare.api.notification.entity.Notification;
import com.pemits.webcare.api.notification.repository.NotificationRepository;
import com.pemits.webcare.api.notification.repository.spec.NotificationSpecBuilder;
import com.pemits.webcare.core.repository.BaseSpecBuilder;
import com.pemits.webcare.core.service.BaseServiceImpl;

/**
* @author Elvin Shrestha on 7/11/2020
*/
@Service
public class NotificationServiceImpl extends BaseServiceImpl<Notification, Long> implements
NotificationService {

protected NotificationServiceImpl(
NotificationRepository repository) {
super(repository);
}

@Override
protected BaseSpecBuilder<Notification> getSpec(Map<String, String> filterParams) {
return new NotificationSpecBuilder(filterParams);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.pemits.webcare.core.config;

import static com.pemits.webcare.core.constant.AppConstant.SOCKET_PUBLISHER;
import static com.pemits.webcare.core.constant.AppConstant.SOCKET_SUBSCRIBER;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

/**
* @author Elvin Shrestha on 7/11/2020
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("webcare-websocket")
.setAllowedOrigins("*")
.withSockJS();
}

@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker(SOCKET_SUBSCRIBER);
registry.setApplicationDestinationPrefixes(SOCKET_PUBLISHER);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
public class AppConstant {

public static final String USERNAME = "username";
public static final String SPRING_MODEL = "spring";
public static final String SUCCESSFUL = "Successful";
public static final String FROM_EMAIL = "[email protected]";
public static final String SOCKET_PUBLISHER = "/webcare-publisher";
public static final String SOCKET_SUBSCRIBER = "/webcare-subscriber";

public static final String NOTIFY_NEW_APPOINTMENT = "You have a new appointment";

private AppConstant() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.pemits.webcare.core.enums;

/**
* @author Elvin Shrestha on 7/11/2020
*/
public enum NotificationStatus {
UNSEEN, SEEN, DELETED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.pemits.webcare.web.notification;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.pemits.webcare.api.notification.entity.Notification;
import com.pemits.webcare.api.notification.service.NotificationService;
import com.pemits.webcare.core.controller.BaseController;

/**
* @author Elvin Shrestha on 7/11/2020
*/
@RestController
@RequestMapping(NotificationController.URL)
@Slf4j
public class NotificationController extends BaseController<Notification, Long> {

static final String URL = "/v1/notification";
private final NotificationService notificationService;

public NotificationController(NotificationService notificationService) {
super(notificationService, log.getClass());

this.notificationService = notificationService;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.pemits.webcare.web.notification;

import java.util.Optional;
import javax.validation.Valid;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.pemits.webcare.api.notification.component.NotificationComponent;
import com.pemits.webcare.api.notification.entity.Notification;
import com.pemits.webcare.core.dto.RestResponseDto;

/**
* @author Elvin Shrestha on 7/11/2020
*/
@RestController
public class WebSocketController {

private final NotificationComponent notificationComponent;

public WebSocketController(
NotificationComponent notificationComponent) {
this.notificationComponent = notificationComponent;
}

@MessageMapping("/send")
public ResponseEntity<?> send(@Valid @RequestBody Notification notification) {
if (notification.getFrom() == null || notification.getTo() == null) {
return new RestResponseDto()
.fail(HttpStatus.BAD_REQUEST, Optional.of("Source or target user is undefined"));
}

Notification saved = notificationComponent.sendAndSaveMessage(notification);

return new RestResponseDto().success(saved);
}
}
Loading

0 comments on commit 185f861

Please sign in to comment.