You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I am building an app, where web push notification are sent. Notifications are user's choice to enable to disable, however, I want to show the user that the notifications are disabled and this would affect apps functionality.
The error handler in the WebPush.java for method public void subscribe(UI ui, WebPushSubscriptionResponse receiver) [highlighting this method as this is where the logs are getting populated] is having a private handler, which can not be accessed, if a user has denied the notification, the server can not react to it.
Also the logs are of no use and are populated on the server, while it appears that the message is for the customer ("You have disabled notifications, you have to enabled them in your settings"). This populates the server logs with errors, while this can not be acted upon. Like for example updating the database with bool notificationGranted.
java.lang.RuntimeException: Unable to execute web push command. JS error is 'Error: You have blocked notifications. You need to manually enable them in your browser.'
at com.vaadin.flow.server.webpush.WebPush.lambda$new$f744fbe0$1(WebPush.java:63) ~[flow-webpush-24.6.0.jar:na]
at com.vaadin.flow.component.internal.PendingJavaScriptInvocation.completeExceptionally(PendingJavaScriptInvocation.java:112) ~[flow-server-24.6.0.jar:24.6.0]
The above shown exception is intended for user, but this can not be accessed by the UI.
Describe the solution you'd like
ExceptionHandler consumer is externalized, so the actual view can react to it, and use the state to update DB show notification etc. Expected Class improvement is attached below.
Another solution could be to not have the errorHandler final, and have a public setExceptionHandler method which will do the same thing that is done in the new constructor of the class.
Describe alternatives you've considered
As an alternative I am calling public void isNotificationDenied(UI ui, WebPushState receiver) and showing the error message and updated the DB, while I do not like this approach as this takes multiple cycles between client and server.
Additional context
public class WebPush {
private PushService pushService;
private String publicKey;
private final SerializableConsumer<String> errorHandler;
public WebPush(String publicKey, String privateKey, String subject) {
this.publicKey = publicKey;
this.errorHandler = getDefaultExceptionHandler();
Security.addProvider(new BouncyCastleProvider());
try {
this.pushService = PushService.builder().withVapidPublicKey(publicKey).withVapidPrivateKey(privateKey).withVapidSubject(subject).build();
} catch (GeneralSecurityException e) {
throw new WebPushException("Security exception initializing web push PushService", e);
}
}
//Adding a second constructor to externalize exception handling
public WebPush(String publicKey, String privateKey, String subject, SerializableConsumer<String> errorHandler) {
this.publicKey = publicKey;
this.errorHandler = errorHandler;
Security.addProvider(new BouncyCastleProvider());
try {
this.pushService = PushService.builder().withVapidPublicKey(publicKey).withVapidPrivateKey(privateKey).withVapidSubject(subject).build();
} catch (GeneralSecurityException e) {
throw new WebPushException("Security exception initializing web push PushService", e);
}
}
private static SerializableConsumer<String> getDefaultExceptionHandler() {
return (err) -> {
throw new RuntimeException("Unable to execute web push command. JS error is '" + err + "'");
};
}
public void sendNotification(WebPushSubscription subscription, WebPushMessage message) throws WebPushException {
int statusCode = -1;
HttpResponse<String> response = null;
try {
Subscription.Keys keys = null;
if (subscription.keys() != null) {
keys = new Subscription.Keys(subscription.keys().p256dh(), subscription.keys().auth());
}
Subscription nativeSubscription = new Subscription(subscription.endpoint(), keys);
Notification notification = Notification.builder().subscription(nativeSubscription).payload(message.toJson()).build();
response = this.pushService.send(notification, PushService.DEFAULT_ENCODING, BodyHandlers.ofString());
statusCode = response.statusCode();
} catch (Exception e) {
this.getLogger().error("Failed to send notification.", e);
throw new WebPushException("Sending of web push notification failed", e);
}
if (statusCode != 201) {
this.getLogger().error("Failed to send web push notification, received status code:" + statusCode);
this.getLogger().error(String.join("\n", (CharSequence)response.body()));
throw new WebPushException("Sending of web push notification failed with status code " + statusCode);
}
}
public void subscriptionExists(UI ui, WebPushState receiver) {
SerializableConsumer<JsonValue> resultHandler = (json) -> receiver.state(Boolean.parseBoolean(json.toJson()));
this.executeJavascript(ui, "return window.Vaadin.Flow.webPush.registrationStatus()").then(resultHandler, this.errorHandler);
}
public void isNotificationDenied(UI ui, WebPushState receiver) {
SerializableConsumer<JsonValue> resultHandler = (json) -> receiver.state(Boolean.parseBoolean(json.toJson()));
this.executeJavascript(ui, "return window.Vaadin.Flow.webPush.notificationDenied()").then(resultHandler, this.errorHandler);
}
public void isNotificationGranted(UI ui, WebPushState receiver) {
SerializableConsumer<JsonValue> resultHandler = (json) -> receiver.state(Boolean.parseBoolean(json.toJson()));
this.executeJavascript(ui, "return window.Vaadin.Flow.webPush.notificationGranted()").then(resultHandler, this.errorHandler);
}
public void subscribe(UI ui, WebPushSubscriptionResponse receiver) {
SerializableConsumer<JsonValue> resultHandler = (json) -> {
JsonObject responseJson = Json.parse(json.toJson());
receiver.subscription(this.generateSubscription(responseJson));
};
this.executeJavascript(ui, "return window.Vaadin.Flow.webPush.subscribe($0)", this.publicKey).then(resultHandler, this.errorHandler);
}
public void unsubscribe(UI ui, WebPushSubscriptionResponse receiver) {
this.executeJavascript(ui, "return window.Vaadin.Flow.webPush.unsubscribe()").then(this.handlePossiblyEmptySubscription(receiver), this.errorHandler);
}
public void fetchExistingSubscription(UI ui, WebPushSubscriptionResponse receiver) {
this.executeJavascript(ui, "return window.Vaadin.Flow.webPush.getSubscription()").then(this.handlePossiblyEmptySubscription(receiver), this.errorHandler);
}
private PendingJavaScriptResult executeJavascript(UI ui, String script, Serializable... parameters) {
this.initWebPushClient(ui);
return ui.getPage().executeJs(script, parameters);
}
private void initWebPushClient(UI ui) {
if (ComponentUtil.getData(ui, "webPushInitialized") == null) {
ComponentUtil.setData(ui, "webPushInitialized", true);
Page page = ui.getPage();
try {
try (InputStream stream = com.vaadin.flow.server.webpush.WebPush.class.getClassLoader().getResourceAsStream("META-INF/frontend/FlowWebPush.js")) {
page.executeJs(StringUtil.removeComments(IOUtils.toString(stream, StandardCharsets.UTF_8)), new Serializable[0]).then((unused) -> this.getLogger().debug("Webpush client code initialized"), (err) -> this.getLogger().error("Webpush client code initialization failed: {}", err));
}
} catch (IOException var8) {
throw new WebPushException("Could not load webpush client code");
}
}
}
private SerializableConsumer<JsonValue> handlePossiblyEmptySubscription(WebPushSubscriptionResponse receiver) {
return (json) -> {
JsonObject responseJson;
if (json.getType() == JsonType.STRING) {
responseJson = Json.createObject();
responseJson.put("message", json.asString());
} else {
responseJson = Json.parse(json.toJson());
}
if (responseJson.hasKey("message")) {
receiver.subscription((WebPushSubscription)null);
} else {
receiver.subscription(this.generateSubscription(responseJson));
}
};
}
private WebPushSubscription generateSubscription(JsonObject subscriptionJson) {
WebPushKeys keys = new WebPushKeys(subscriptionJson.getObject("keys").getString("p256dh"), subscriptionJson.getObject("keys").getString("auth"));
return new WebPushSubscription(subscriptionJson.getString("endpoint"), keys);
}
private Logger getLogger() {
return LoggerFactory.getLogger(com.vaadin.flow.server.webpush.WebPush.class);
}
}
The text was updated successfully, but these errors were encountered:
Describe your motivation
I am building an app, where web push notification are sent. Notifications are user's choice to enable to disable, however, I want to show the user that the notifications are disabled and this would affect apps functionality.
The error handler in the WebPush.java for method
public void subscribe(UI ui, WebPushSubscriptionResponse receiver)
[highlighting this method as this is where the logs are getting populated] is having a private handler, which can not be accessed, if a user has denied the notification, the server can not react to it.Also the logs are of no use and are populated on the server, while it appears that the message is for the customer ("You have disabled notifications, you have to enabled them in your settings"). This populates the server logs with errors, while this can not be acted upon. Like for example updating the database with bool notificationGranted.
The above shown exception is intended for user, but this can not be accessed by the UI.
Describe the solution you'd like
ExceptionHandler consumer is externalized, so the actual view can react to it, and use the state to update DB show notification etc. Expected Class improvement is attached below.
Another solution could be to not have the errorHandler final, and have a public setExceptionHandler method which will do the same thing that is done in the new constructor of the class.
Describe alternatives you've considered
As an alternative I am calling
public void isNotificationDenied(UI ui, WebPushState receiver)
and showing the error message and updated the DB, while I do not like this approach as this takes multiple cycles between client and server.Additional context
The text was updated successfully, but these errors were encountered: