Push-notifications to mobile
This commit is contained in:
parent
25c4f309a7
commit
9add2962a6
@ -354,6 +354,10 @@
|
|||||||
<groupId>com.slack.api</groupId>
|
<groupId>com.slack.api</groupId>
|
||||||
<artifactId>slack-api-client</artifactId>
|
<artifactId>slack-api-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.firebase</groupId>
|
||||||
|
<artifactId>firebase-admin</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@ -149,7 +149,7 @@ public class SystemInfoController extends BaseController {
|
|||||||
infoObject.put("artifact", buildProperties.getArtifact());
|
infoObject.put("artifact", buildProperties.getArtifact());
|
||||||
infoObject.put("name", buildProperties.getName());
|
infoObject.put("name", buildProperties.getName());
|
||||||
} else {
|
} else {
|
||||||
infoObject.put("version", "unknown");
|
infoObject.put("version", "3.5.5");
|
||||||
}
|
}
|
||||||
return infoObject;
|
return infoObject;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.service.notification.channels;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.google.auth.oauth2.GoogleCredentials;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.firebase.FirebaseApp;
|
||||||
|
import com.google.firebase.FirebaseOptions;
|
||||||
|
import com.google.firebase.messaging.FirebaseMessaging;
|
||||||
|
import com.google.firebase.messaging.Message;
|
||||||
|
import com.google.firebase.messaging.Notification;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.thingsboard.server.common.data.User;
|
||||||
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
|
import org.thingsboard.server.common.data.notification.settings.MobileNotificationDeliveryMethodConfig;
|
||||||
|
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
||||||
|
import org.thingsboard.server.common.data.notification.template.MobileDeliveryMethodNotificationTemplate;
|
||||||
|
import org.thingsboard.server.dao.notification.NotificationSettingsService;
|
||||||
|
import org.thingsboard.server.service.executors.ExternalCallExecutorService;
|
||||||
|
import org.thingsboard.server.service.notification.NotificationProcessingContext;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MobileNotificationChannel implements NotificationChannel<User, MobileDeliveryMethodNotificationTemplate> {
|
||||||
|
|
||||||
|
private final ExternalCallExecutorService executor;
|
||||||
|
private final NotificationSettingsService notificationSettingsService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<Void> sendNotification(User recipient, MobileDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) {
|
||||||
|
String fcmToken = Optional.ofNullable(recipient.getAdditionalInfo())
|
||||||
|
.map(info -> info.get("fcmToken")).filter(JsonNode::isTextual).map(JsonNode::asText)
|
||||||
|
.orElse(null);
|
||||||
|
if (StringUtils.isEmpty(fcmToken)) {
|
||||||
|
return Futures.immediateFailedFuture(new RuntimeException("User doesn't have the mobile app installed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
MobileNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE);
|
||||||
|
return executor.submit(() -> {
|
||||||
|
FirebaseOptions firebaseOptions = FirebaseOptions.builder()
|
||||||
|
.setCredentials(GoogleCredentials.fromStream(IOUtils.toInputStream(config.getFirebaseServiceAccountCredentials(), StandardCharsets.UTF_8)))
|
||||||
|
.build();
|
||||||
|
String appName = ctx.getTenantId().toString();
|
||||||
|
|
||||||
|
FirebaseApp firebaseApp = FirebaseApp.getApps().stream()
|
||||||
|
.filter(app -> app.getName().equals(appName))
|
||||||
|
.findFirst().orElseGet(() -> {
|
||||||
|
try {
|
||||||
|
return FirebaseApp.initializeApp(firebaseOptions, appName);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
return FirebaseApp.getInstance(appName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
FirebaseMessaging firebaseMessaging;
|
||||||
|
try {
|
||||||
|
firebaseMessaging = FirebaseMessaging.getInstance(firebaseApp);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// because of concurrency issues: FirebaseMessaging.getInstance lazily loads FirebaseMessagingService
|
||||||
|
firebaseMessaging = FirebaseMessaging.getInstance(firebaseApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Message message = Message.builder()
|
||||||
|
.setNotification(Notification.builder()
|
||||||
|
.setTitle(processedTemplate.getSubject())
|
||||||
|
.setBody(processedTemplate.getBody())
|
||||||
|
.build())
|
||||||
|
.setToken(fcmToken)
|
||||||
|
.build();
|
||||||
|
firebaseMessaging.send(message);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void check(TenantId tenantId) throws Exception {
|
||||||
|
NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);
|
||||||
|
if (!settings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.MOBILE)) {
|
||||||
|
throw new RuntimeException("Push-notifications to mobile are not configured");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NotificationDeliveryMethod getDeliveryMethod() {
|
||||||
|
return NotificationDeliveryMethod.MOBILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -24,7 +24,8 @@ public enum NotificationDeliveryMethod {
|
|||||||
WEB("web"),
|
WEB("web"),
|
||||||
EMAIL("email"),
|
EMAIL("email"),
|
||||||
SMS("SMS"),
|
SMS("SMS"),
|
||||||
SLACK("Slack");
|
SLACK("Slack"),
|
||||||
|
MOBILE("mobile");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.common.data.notification.settings;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class MobileNotificationDeliveryMethodConfig implements NotificationDeliveryMethodConfig {
|
||||||
|
|
||||||
|
private String firebaseServiceAccountCredentialsFileName;
|
||||||
|
@NotEmpty
|
||||||
|
private String firebaseServiceAccountCredentials;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NotificationDeliveryMethod getMethod() {
|
||||||
|
return NotificationDeliveryMethod.MOBILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -27,7 +27,8 @@ import java.io.Serializable;
|
|||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method")
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method")
|
||||||
@JsonSubTypes({
|
@JsonSubTypes({
|
||||||
@Type(name = "SLACK", value = SlackNotificationDeliveryMethodConfig.class)
|
@Type(name = "SLACK", value = SlackNotificationDeliveryMethodConfig.class),
|
||||||
|
@Type(name = "MOBILE", value = MobileNotificationDeliveryMethodConfig.class)
|
||||||
})
|
})
|
||||||
public interface NotificationDeliveryMethodConfig extends Serializable {
|
public interface NotificationDeliveryMethodConfig extends Serializable {
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import java.util.Set;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public enum NotificationTargetType {
|
public enum NotificationTargetType {
|
||||||
|
|
||||||
PLATFORM_USERS(Set.of(NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL, NotificationDeliveryMethod.SMS)),
|
PLATFORM_USERS(Set.of(NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL, NotificationDeliveryMethod.SMS, NotificationDeliveryMethod.MOBILE)),
|
||||||
SLACK(Set.of(NotificationDeliveryMethod.SLACK));
|
SLACK(Set.of(NotificationDeliveryMethod.SLACK));
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
|||||||
@ -33,7 +33,8 @@ import javax.validation.constraints.NotEmpty;
|
|||||||
@Type(name = "WEB", value = WebDeliveryMethodNotificationTemplate.class),
|
@Type(name = "WEB", value = WebDeliveryMethodNotificationTemplate.class),
|
||||||
@Type(name = "EMAIL", value = EmailDeliveryMethodNotificationTemplate.class),
|
@Type(name = "EMAIL", value = EmailDeliveryMethodNotificationTemplate.class),
|
||||||
@Type(name = "SMS", value = SmsDeliveryMethodNotificationTemplate.class),
|
@Type(name = "SMS", value = SmsDeliveryMethodNotificationTemplate.class),
|
||||||
@Type(name = "SLACK", value = SlackDeliveryMethodNotificationTemplate.class)
|
@Type(name = "SLACK", value = SlackDeliveryMethodNotificationTemplate.class),
|
||||||
|
@Type(name = "MOBILE", value = MobileDeliveryMethodNotificationTemplate.class)
|
||||||
})
|
})
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
|||||||
@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.common.data.notification.template;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.ToString;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class MobileDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
|
||||||
|
|
||||||
|
@NotEmpty
|
||||||
|
private String subject;
|
||||||
|
|
||||||
|
public MobileDeliveryMethodNotificationTemplate(MobileDeliveryMethodNotificationTemplate other) {
|
||||||
|
super(other);
|
||||||
|
this.subject = other.subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NotificationDeliveryMethod getMethod() {
|
||||||
|
return NotificationDeliveryMethod.MOBILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MobileDeliveryMethodNotificationTemplate copy() {
|
||||||
|
return new MobileDeliveryMethodNotificationTemplate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAny(String... params) {
|
||||||
|
return super.containsAny(params) || StringUtils.containsAny(subject, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
6
pom.xml
6
pom.xml
@ -150,6 +150,7 @@
|
|||||||
<allure-testng.version>2.21.0</allure-testng.version>
|
<allure-testng.version>2.21.0</allure-testng.version>
|
||||||
<allure-maven.version>2.12.0</allure-maven.version>
|
<allure-maven.version>2.12.0</allure-maven.version>
|
||||||
<slack-api.version>1.12.1</slack-api.version>
|
<slack-api.version>1.12.1</slack-api.version>
|
||||||
|
<firebase-admin.version>8.0.1</firebase-admin.version>
|
||||||
<oshi.version>3.4.0</oshi.version>
|
<oshi.version>3.4.0</oshi.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@ -1987,6 +1988,11 @@
|
|||||||
<artifactId>slack-api-client</artifactId>
|
<artifactId>slack-api-client</artifactId>
|
||||||
<version>${slack-api.version}</version>
|
<version>${slack-api.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.firebase</groupId>
|
||||||
|
<artifactId>firebase-admin</artifactId>
|
||||||
|
<version>${firebase-admin.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jgit</groupId>
|
<groupId>org.eclipse.jgit</groupId>
|
||||||
<artifactId>org.eclipse.jgit</artifactId>
|
<artifactId>org.eclipse.jgit</artifactId>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user