1
0

Rework push server so it holds the subscriptions in a DB

This commit is contained in:
2020-07-27 23:22:32 +02:00
parent f3c6227dce
commit a8b2a43216
26 changed files with 554 additions and 121 deletions

View File

@@ -16,10 +16,6 @@
<name>push-service-client-lib</name>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
@@ -28,6 +24,16 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Misc dependencies -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
</dependencies>
<build>

View File

@@ -5,7 +5,7 @@
2. How to use the client-lib in a client application
====================================================
- Include property 'push-service-client.serverUrl=' and set it to the path to the server part
- (optional) Enable loggin via property 'logging.level.de.pushservice=DEBUG'
- (optional) Enable logging via property 'logging.level.de.pushservice=DEBUG'
- Enable component scan in app configuration via '@ComponentScan("de.pushservice.client")'
- Obtain an instance of SubscriptionService via '@Autowired'
- Add a JavaScript file with content 'registerServiceWorker();' to register the service worker and subscribing at the

View File

@@ -5,6 +5,7 @@ import org.springframework.http.ResponseEntity;
public enum ResponseReason {
OK(HttpStatus.OK),
EMPTY_SCOPE(HttpStatus.BAD_REQUEST),
UNKNOWN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR);
private final HttpStatus httpStatus;

View File

@@ -1,6 +1,5 @@
package de.pushservice.client.controller;
import de.pushservice.client.ResponseReason;
import de.pushservice.client.dto.SubscriptionDto;
import de.pushservice.client.service.SubscriptionService;
import org.slf4j.Logger;
@@ -22,8 +21,6 @@ public class SubscriptionController {
public ResponseEntity subscribe(@RequestBody SubscriptionDto subscriptionDto) {
LOGGER.debug(String.format("Received subscription for endpoint %s", subscriptionDto.getEndpoint()));
this.subscriptionService.subscribe(subscriptionDto);
return ResponseReason.OK.toResponseEntity();
return this.subscriptionService.subscribe(subscriptionDto).toResponseEntity();
}
}

View File

@@ -1,20 +1,20 @@
package de.pushservice.client.dto;
public class NotificationRequestDto {
private SubscriptionDto subscriptionDto;
private String scope;
private MessageDto messageDto;
public NotificationRequestDto(SubscriptionDto subscriptionDto, MessageDto messageDto) {
this.subscriptionDto = subscriptionDto;
public NotificationRequestDto(String scope, MessageDto messageDto) {
this.scope = scope;
this.messageDto = messageDto;
}
public SubscriptionDto getSubscriptionDto() {
return subscriptionDto;
public String getScope() {
return scope;
}
public void setSubscriptionDto(SubscriptionDto subscriptionDto) {
this.subscriptionDto = subscriptionDto;
public void setScope(String scope) {
this.scope = scope;
}
public MessageDto getMessageDto() {

View File

@@ -4,6 +4,7 @@ public class SubscriptionDto {
private String auth;
private String endpoint;
private String key;
private String scope;
public void setAuth(String auth) {
this.auth = auth;
@@ -28,4 +29,12 @@ public class SubscriptionDto {
public String getEndpoint() {
return endpoint;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}

View File

@@ -9,6 +9,7 @@ import de.pushservice.client.dto.NotificationRequestDto;
import de.pushservice.client.dto.PayloadDto;
import de.pushservice.client.dto.SubscriptionDto;
import de.pushservice.client.model.Urgency;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -30,37 +31,41 @@ public class SubscriptionService {
@Autowired
private ObjectMapper objectMapper;
private Set<SubscriptionDto> subscriptions = new HashSet<>();
private String scope;
public void subscribe(SubscriptionDto subscriptionDto) {
this.subscriptions.add(subscriptionDto);
public boolean isInitialized() {
return StringUtils.isNotEmpty(this.scope);
}
public int notifyAll(PayloadDto payload, String topic, Urgency urgency) throws JsonProcessingException {
int notificationsSend = 0;
final Urgency tmpUrgency = Optional.ofNullable(urgency).orElse(Urgency.NORMAL);
final MessageDto messageDto = new MessageDto(objectMapper.writeValueAsString(payload), topic, tmpUrgency);
for (SubscriptionDto subscription : this.subscriptions) {
final NotificationRequestDto requestDto = new NotificationRequestDto(subscription, messageDto);
LOGGER.debug(String.format("Sending notification for endpoint %s", subscription.getEndpoint()));
final ResponseEntity<String> responseEntity = new RestTemplate()
.exchange(this.config.getServerUrl(), HttpMethod.POST, new HttpEntity<>(requestDto), String.class);
final ResponseReason responseReason = ResponseReason.fromResponseEntity(responseEntity);
if (ResponseReason.OK == responseReason) {
notificationsSend++;
}
else {
// Well, nothing we can do about it
LOGGER.error("Sending notification to endpoint failed! %s", responseReason);
public ResponseReason subscribe(SubscriptionDto subscriptionDto) {
if (StringUtils.isEmpty(this.scope)) {
this.scope = subscriptionDto.getScope();
}
else {
if(!this.scope.equals(subscriptionDto.getScope())) {
// Should not happen since the scope is the host + context path of the hosting app
LOGGER.warn(String.format("Scope changed! Old: %s, new %s", this.scope, subscriptionDto.getScope()));
}
}
return notificationsSend;
final ResponseEntity<String> responseEntity = new RestTemplate()
.exchange(this.config.getServerUrl() + "subscribe", HttpMethod.POST, new HttpEntity<>(subscriptionDto), String.class);
return ResponseReason.fromResponseEntity(responseEntity);
}
public void notify(PayloadDto payload, String topic, Urgency urgency) throws JsonProcessingException {
final Urgency tmpUrgency = Optional.ofNullable(urgency).orElse(Urgency.NORMAL);
final MessageDto messageDto = new MessageDto(objectMapper.writeValueAsString(payload), topic, tmpUrgency);
final NotificationRequestDto requestDto = new NotificationRequestDto(this.scope, messageDto);
final ResponseEntity<String> responseEntity = new RestTemplate()
.exchange(this.config.getServerUrl() + "notify", HttpMethod.POST, new HttpEntity<>(requestDto), String.class);
final ResponseReason responseReason = ResponseReason.fromResponseEntity(responseEntity);
if (ResponseReason.OK != responseReason) {
LOGGER.error(String.format("Could not send notification! %s", responseReason));
}
}
}

View File

@@ -5,7 +5,7 @@ function registerServiceWorker() {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
initializeState();
initializeState(registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
@@ -14,7 +14,7 @@ function registerServiceWorker() {
}
}
function initializeState() {
function initializeState(registrationScope) {
if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
console.warn('Notifications aren\'t supported.');
return;
@@ -33,11 +33,11 @@ function initializeState() {
navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.getSubscription().then(function (subscription) {
if (!subscription) {
subscribe();
subscribe(registrationScope);
return;
}
sendSubscriptionToServer(subscription);
sendSubscriptionToServer(subscription, registrationScope);
})
.catch(function(err) {
console.warn('Error during getSubscription()', err);
@@ -45,10 +45,10 @@ function initializeState() {
});
}
function subscribe() {
function subscribe(registrationScope) {
navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly: true}).then(function (subscription) {
return sendSubscriptionToServer(subscription);
return sendSubscriptionToServer(subscription, registrationScope);
})
.catch(function (e) {
if (Notification.permission === 'denied') {
@@ -60,7 +60,7 @@ function subscribe() {
});
}
function sendSubscriptionToServer(subscription) {
function sendSubscriptionToServer(subscription, registrationScope) {
var key = subscription.getKey ? subscription.getKey('p256dh') : '';
var auth = subscription.getKey ? subscription.getKey('auth') : '';
@@ -76,7 +76,8 @@ function sendSubscriptionToServer(subscription) {
// Take byte[] and turn it into a base64 encoded string suitable for
// POSTing to a server over HTTP
key: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : '',
auth: auth ? btoa(String.fromCharCode.apply(null, new Uint8Array(auth))) : ''
auth: auth ? btoa(String.fromCharCode.apply(null, new Uint8Array(auth))) : '',
scope: registrationScope
})
});
}