Skip to content

Commit

Permalink
Merge pull request #30 from stratospheric-dev/feature/27_Implement_pu…
Browse files Browse the repository at this point in the history
…sh_notifications

Feature/27 implement push notifications
  • Loading branch information
BjoernKW authored Oct 7, 2020
2 parents cb4cff8 + 7577f52 commit 980149d
Show file tree
Hide file tree
Showing 52 changed files with 871 additions and 187 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ HELP.md
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**
!**/src/test/**
!**/src/test/**
/gradle/
12 changes: 9 additions & 3 deletions application/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id 'org.springframework.boot' version '2.3.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'org.springframework.boot' version '2.3.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}

Expand All @@ -13,7 +13,7 @@ repositories {
}

ext {
set('springCloudVersion', 'Hoxton.SR5')
set('springCloudVersion', 'Hoxton.SR6')
}

dependencies {
Expand All @@ -25,11 +25,17 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.cloud:spring-cloud-starter-aws'
implementation 'org.springframework.cloud:spring-cloud-aws-autoconfigure'
implementation 'org.springframework.cloud:spring-cloud-starter-aws-messaging'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.webjars:webjars-locator-core'
implementation 'org.webjars:sockjs-client:1.1.2'
implementation 'org.webjars:stomp-websocket:2.3.3-1'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.flywaydb:flyway-core'
implementation 'com.amazonaws:aws-java-sdk-cognitoidp'
implementation 'com.amazonaws:aws-java-sdk-ses'
implementation 'com.google.code.findbugs:jsr305:3.0.2'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'org.postgresql:postgresql'
Expand Down
26 changes: 22 additions & 4 deletions application/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
version: '3.3'

services:

postgres:
container_name: "aws101-postgres"
image: postgres
volumes:
- blogtrack-postgres-data:/var/lib/postgresql/data
- stratospheric-postgres-data:/var/lib/postgresql/data
ports:
- 5432:5432
environment:
- POSTGRES_USER=aws101
- POSTGRES_PASSWORD=aws101
- POSTGRES_DB=aws101

localstack:
container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
image: localstack/localstack
network_mode: bridge
ports:
- "4566:4566"
- "4571:4571"
- "${PORT_WEB_UI-8081}:${PORT_WEB_UI-8080}"
environment:
- SERVICES=${SERVICES- }
- DEBUG=${DEBUG- }
- DATA_DIR=${DATA_DIR- }
- PORT_WEB_UI=${PORT_WEB_UI-8080}
- LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
- KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
- DOCKER_HOST=unix:///var/run/docker.sock
- HOST_TMP_FOLDER=${TMPDIR}
volumes:
- "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
volumes:
blogtrack-postgres-data:
stratospheric-postgres-data:
driver: local
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package dev.aws101.collaboration;

import dev.aws101.person.Person;
import dev.aws101.person.PersonRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.messaging.config.annotation.NotificationMessage;
import org.springframework.cloud.aws.messaging.config.annotation.NotificationSubject;
import org.springframework.cloud.aws.messaging.endpoint.NotificationStatus;
import org.springframework.cloud.aws.messaging.endpoint.annotation.NotificationMessageMapping;
import org.springframework.cloud.aws.messaging.endpoint.annotation.NotificationSubscriptionMapping;
import org.springframework.cloud.aws.messaging.endpoint.annotation.NotificationUnsubscribeConfirmationMapping;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.socket.messaging.WebSocketStompClient;

import java.security.Principal;
import java.util.concurrent.ExecutionException;

@Controller
@RequestMapping("${custom.updates-topic}")
public class PubSubController {

private static final Logger LOG = LoggerFactory.getLogger(PubSubController.class.getName());

private static final String UPDATE_TODO_URL = "/websocketEndpoints/updateTodo";

private final PersonRepository personRepository;

private final WebSocketStompClient webSocketStompClient;

@Value("${custom.websocket-url}")
private String webSocketURL;

public PubSubController(PersonRepository personRepository, WebSocketStompClient webSocketStompClient) {
this.personRepository = personRepository;
this.webSocketStompClient = webSocketStompClient;
}

@NotificationSubscriptionMapping
public void confirmSubscriptionMessage(NotificationStatus notificationStatus) {
notificationStatus.confirmSubscription();
}

@NotificationUnsubscribeConfirmationMapping
public void confirmUnsubscribeMessage(NotificationStatus notificationStatus) {
notificationStatus.confirmSubscription();
}

@NotificationMessageMapping
public void receiveNotification(
@NotificationSubject String subject,
@NotificationMessage String message,
Principal principal
) {
LOG.info("Todo update received. Subject '{}': {}", subject, message);

Person person = personRepository.findByName("Admin").orElse(null);
if (principal != null) {
person = personRepository.findByName(principal.getName()).orElse(null);
}

if (person != null && person.getEmail().equals(subject)) {
try {
StompSession stompSession = webSocketStompClient.connect(webSocketURL, new RelayStompSessionHandler()).get();
stompSession.send(UPDATE_TODO_URL, message);
} catch (InterruptedException e) {
LOG.error(e.getMessage());
Thread.currentThread().interrupt();
} catch (ExecutionException ee) {
LOG.error("ExecutionException: ", ee);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package dev.aws101.collaboration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.stereotype.Component;

import java.lang.reflect.Type;

@Component
public class RelayStompSessionHandler extends StompSessionHandlerAdapter {

private static final Logger LOG = LoggerFactory.getLogger(RelayStompSessionHandler.class.getName());

@Override
public void afterConnected(StompSession session, @Nullable StompHeaders connectedHeaders) {
LOG.info("New session established: {}", session.getSessionId());

session.subscribe("/topic/todoUpdates", this);

LOG.info("Subscribed to /topic/todoUpdates");
}

@Override
public void handleException(
@Nullable StompSession session,
StompCommand command,
@Nullable StompHeaders headers,
@Nullable byte[] payload,
@Nullable Throwable exception
) {
LOG.error("An exception occured.", exception);
}

@Override
public Type getPayloadType(@Nullable StompHeaders headers) {
return String.class;
}

@Override
public void handleFrame(@Nullable StompHeaders headers, Object payload) {
String message = (String) payload;

LOG.info("Message received: {}", message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package dev.aws101.collaboration;

import dev.aws101.todo.TodoTestCollaborationService;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/testTodo")
@Profile("dev")
public class TestTodoController {

private final TodoTestCollaborationService todoTestCollaborationService;

public TestTodoController(
TodoTestCollaborationService todoTestCollaborationService
) {
this.todoTestCollaborationService = todoTestCollaborationService;
}

@GetMapping("/confirmCollaboration")
@ResponseBody
public String confirmCollaboration() {
String subject = todoTestCollaborationService.testConfirmCollaboration();

return "Done: " + subject;
}
}
Original file line number Diff line number Diff line change
@@ -1,85 +1,67 @@
package dev.aws101.collaboration;

import dev.aws101.todo.Priority;
import dev.aws101.person.Person;
import dev.aws101.todo.Todo;

import java.time.LocalDate;
import javax.persistence.*;

@Entity
public class TodoCollaborationRequest {

private Long todoId;
private String todoTitle;
private String todoDescription;
private Priority todoPriority;
private Long collaboratorId;
private String collaboratorName;
private String collaboratorEmail;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

public Long getTodoId() {
return todoId;
}
private String token;

public void setTodoId(Long todoId) {
this.todoId = todoId;
}
@ManyToOne
private Person collaborator;

public String getTodoTitle() {
return todoTitle;
}

public void setTodoTitle(String todoTitle) {
this.todoTitle = todoTitle;
}

public String getTodoDescription() {
return todoDescription;
}

public void setTodoDescription(String todoDescription) {
this.todoDescription = todoDescription;
}
@ManyToOne
private Todo todo;

public Priority getTodoPriority() {
return todoPriority;
public Long getId() {
return id;
}

public void setTodoPriority(Priority todoPriority) {
this.todoPriority = todoPriority;
public void setId(Long id) {
this.id = id;
}

public Long getCollaboratorId() {
return collaboratorId;
public String getToken() {
return token;
}

public void setCollaboratorId(Long collaboratorId) {
this.collaboratorId = collaboratorId;
public void setToken(String token) {
this.token = token;
}

public String getCollaboratorName() {
return collaboratorName;
public Person getCollaborator() {
return collaborator;
}

public void setCollaboratorName(String collaboratorName) {
this.collaboratorName = collaboratorName;
public void setCollaborator(Person collaborator) {
this.collaborator = collaborator;
}

public String getCollaboratorEmail() {
return collaboratorEmail;
public Todo getTodo() {
return todo;
}

public void setCollaboratorEmail(String collaboratorEmail) {
this.collaboratorEmail = collaboratorEmail;
public void setTodo(Todo todo) {
this.todo = todo;
}

@Override
public String toString() {
return "TodoCollaborationRequest{" +
"todoId=" + todoId +
", todoTitle='" + todoTitle + '\'' +
", todoDescription='" + todoDescription + '\'' +
", todoPriority=" + todoPriority +
", collaboratorId=" + collaboratorId +
", collaboratorName='" + collaboratorName + '\'' +
", collaboratorEmail='" + collaboratorEmail + '\'' +
"todoId=" + todo.getId() +
", todoTitle='" + todo.getTitle() + '\'' +
", todoDescription='" + todo.getDescription() + '\'' +
", todoPriority=" + todo.getPriority() +
", collaboratorId=" + collaborator.getId() +
", collaboratorName='" + collaborator.getName() + '\'' +
", collaboratorEmail='" + collaborator.getEmail() + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.aws101.collaboration;

import dev.aws101.person.Person;
import dev.aws101.todo.Todo;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface TodoCollaborationRequestRepository extends CrudRepository<TodoCollaborationRequest, Long> {

Optional<TodoCollaborationRequest> findByTodoAndCollaborator(Todo todo, Person person);
}
Loading

0 comments on commit 980149d

Please sign in to comment.