Firebase usage refactoring
This commit is contained in:
parent
f501ec4d8d
commit
c5fcc7a1bc
@ -32,7 +32,7 @@ import org.thingsboard.common.util.JacksonUtil;
|
|||||||
import org.thingsboard.rule.engine.api.MailService;
|
import org.thingsboard.rule.engine.api.MailService;
|
||||||
import org.thingsboard.rule.engine.api.NotificationCenter;
|
import org.thingsboard.rule.engine.api.NotificationCenter;
|
||||||
import org.thingsboard.rule.engine.api.SmsService;
|
import org.thingsboard.rule.engine.api.SmsService;
|
||||||
import org.thingsboard.rule.engine.api.slack.SlackService;
|
import org.thingsboard.rule.engine.api.notification.SlackService;
|
||||||
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
|
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
|
||||||
import org.thingsboard.script.api.js.JsInvokeService;
|
import org.thingsboard.script.api.js.JsInvokeService;
|
||||||
import org.thingsboard.script.api.tbel.TbelInvokeService;
|
import org.thingsboard.script.api.tbel.TbelInvokeService;
|
||||||
@ -115,7 +115,6 @@ import org.thingsboard.server.service.transport.TbCoreToTransportService;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|||||||
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.actors.ruleChain;
|
package org.thingsboard.server.actors.ruleChain;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
@ -35,7 +34,7 @@ import org.thingsboard.rule.engine.api.ScriptEngine;
|
|||||||
import org.thingsboard.rule.engine.api.SmsService;
|
import org.thingsboard.rule.engine.api.SmsService;
|
||||||
import org.thingsboard.rule.engine.api.TbContext;
|
import org.thingsboard.rule.engine.api.TbContext;
|
||||||
import org.thingsboard.rule.engine.api.TbRelationTypes;
|
import org.thingsboard.rule.engine.api.TbRelationTypes;
|
||||||
import org.thingsboard.rule.engine.api.slack.SlackService;
|
import org.thingsboard.rule.engine.api.notification.SlackService;
|
||||||
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
|
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
|
||||||
import org.thingsboard.rule.engine.util.TenantIdLoader;
|
import org.thingsboard.rule.engine.util.TenantIdLoader;
|
||||||
import org.thingsboard.server.actors.ActorSystemContext;
|
import org.thingsboard.server.actors.ActorSystemContext;
|
||||||
|
|||||||
@ -29,7 +29,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.thingsboard.rule.engine.api.slack.SlackService;
|
import org.thingsboard.rule.engine.api.notification.SlackService;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||||
import org.thingsboard.server.common.data.id.NotificationTemplateId;
|
import org.thingsboard.server.common.data.id.NotificationTemplateId;
|
||||||
|
|||||||
@ -16,16 +16,10 @@
|
|||||||
package org.thingsboard.server.service.notification.channels;
|
package org.thingsboard.server.service.notification.channels;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.google.auth.oauth2.GoogleCredentials;
|
|
||||||
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 lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.thingsboard.rule.engine.api.notification.FirebaseService;
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
@ -35,13 +29,13 @@ import org.thingsboard.server.common.data.notification.template.MobileDeliveryMe
|
|||||||
import org.thingsboard.server.dao.notification.NotificationSettingsService;
|
import org.thingsboard.server.dao.notification.NotificationSettingsService;
|
||||||
import org.thingsboard.server.service.notification.NotificationProcessingContext;
|
import org.thingsboard.server.service.notification.NotificationProcessingContext;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class MobileNotificationChannel implements NotificationChannel<User, MobileDeliveryMethodNotificationTemplate> {
|
public class MobileNotificationChannel implements NotificationChannel<User, MobileDeliveryMethodNotificationTemplate> {
|
||||||
|
|
||||||
|
private final FirebaseService firebaseService;
|
||||||
private final NotificationSettingsService notificationSettingsService;
|
private final NotificationSettingsService notificationSettingsService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -54,36 +48,8 @@ public class MobileNotificationChannel implements NotificationChannel<User, Mobi
|
|||||||
}
|
}
|
||||||
|
|
||||||
MobileNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE);
|
MobileNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE);
|
||||||
FirebaseOptions firebaseOptions = FirebaseOptions.builder()
|
firebaseService.sendMessage(ctx.getTenantId(), config.getFirebaseServiceAccountCredentials(),
|
||||||
.setCredentials(GoogleCredentials.fromStream(IOUtils.toInputStream(config.getFirebaseServiceAccountCredentials(), StandardCharsets.UTF_8)))
|
fcmToken, processedTemplate.getSubject(), processedTemplate.getBody());
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -17,7 +17,7 @@ package org.thingsboard.server.service.notification.channels;
|
|||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.thingsboard.rule.engine.api.slack.SlackService;
|
import org.thingsboard.rule.engine.api.notification.SlackService;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
||||||
|
|||||||
@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* 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.provider;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import com.github.benmanes.caffeine.cache.RemovalCause;
|
||||||
|
import com.google.auth.oauth2.GoogleCredentials;
|
||||||
|
import com.google.firebase.FirebaseApp;
|
||||||
|
import com.google.firebase.FirebaseOptions;
|
||||||
|
import com.google.firebase.messaging.FirebaseMessaging;
|
||||||
|
import com.google.firebase.messaging.FirebaseMessagingException;
|
||||||
|
import com.google.firebase.messaging.Message;
|
||||||
|
import com.google.firebase.messaging.Notification;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.thingsboard.rule.engine.api.notification.FirebaseService;
|
||||||
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DefaultFirebaseService implements FirebaseService {
|
||||||
|
|
||||||
|
private final Cache<String, FirebaseContext> contexts = Caffeine.newBuilder()
|
||||||
|
.expireAfterAccess(1, TimeUnit.DAYS)
|
||||||
|
.<String, FirebaseContext>removalListener((key, context, cause) -> {
|
||||||
|
if (cause == RemovalCause.EXPIRED && context != null) {
|
||||||
|
context.destroy();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body) {
|
||||||
|
FirebaseContext firebaseContext = contexts.asMap().compute(tenantId.toString(), (key, context) -> {
|
||||||
|
if (context == null) {
|
||||||
|
return new FirebaseContext(key, credentials);
|
||||||
|
} else {
|
||||||
|
context.check(credentials);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Message message = Message.builder()
|
||||||
|
.setNotification(Notification.builder()
|
||||||
|
.setTitle(title)
|
||||||
|
.setBody(body)
|
||||||
|
.build())
|
||||||
|
.setToken(fcmToken)
|
||||||
|
.build();
|
||||||
|
try {
|
||||||
|
firebaseContext.getMessaging().send(message);
|
||||||
|
} catch (FirebaseMessagingException e) {
|
||||||
|
throw new RuntimeException("Failed to send message via FCM: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FirebaseContext {
|
||||||
|
private final String key;
|
||||||
|
private String credentials;
|
||||||
|
private FirebaseApp app;
|
||||||
|
@Getter
|
||||||
|
private FirebaseMessaging messaging;
|
||||||
|
|
||||||
|
public FirebaseContext(String key, String credentials) {
|
||||||
|
this.key = key;
|
||||||
|
this.credentials = credentials;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
FirebaseOptions options;
|
||||||
|
try {
|
||||||
|
options = FirebaseOptions.builder()
|
||||||
|
.setCredentials(GoogleCredentials.fromStream(IOUtils.toInputStream(credentials, StandardCharsets.UTF_8)))
|
||||||
|
.build();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to process service account credentials: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
app = FirebaseApp.initializeApp(options, key);
|
||||||
|
} catch (IllegalStateException alreadyExists) { // should never normally happen
|
||||||
|
app = FirebaseApp.getInstance(key);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
messaging = FirebaseMessaging.getInstance(app);
|
||||||
|
} catch (IllegalStateException alreadyExists) { // should never normally happen
|
||||||
|
messaging = FirebaseMessaging.getInstance(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void check(String credentials) {
|
||||||
|
if (!this.credentials.equals(credentials)) {
|
||||||
|
app.delete();
|
||||||
|
this.credentials = credentials;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
app.delete();
|
||||||
|
app = null;
|
||||||
|
messaging = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.slack;
|
package org.thingsboard.server.service.notification.provider;
|
||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Cache;
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
@ -29,7 +29,7 @@ import com.slack.api.methods.response.users.UsersListResponse;
|
|||||||
import com.slack.api.model.ConversationType;
|
import com.slack.api.model.ConversationType;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.rule.engine.api.slack.SlackService;
|
import org.thingsboard.rule.engine.api.notification.SlackService;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
||||||
@ -22,7 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.data.util.Pair;
|
import org.springframework.data.util.Pair;
|
||||||
import org.thingsboard.rule.engine.api.MailService;
|
import org.thingsboard.rule.engine.api.MailService;
|
||||||
import org.thingsboard.rule.engine.api.slack.SlackService;
|
import org.thingsboard.rule.engine.api.notification.SlackService;
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.id.NotificationRequestId;
|
import org.thingsboard.server.common.data.id.NotificationRequestId;
|
||||||
import org.thingsboard.server.common.data.id.NotificationTargetId;
|
import org.thingsboard.server.common.data.id.NotificationTargetId;
|
||||||
|
|||||||
@ -17,7 +17,7 @@ package org.thingsboard.rule.engine.api;
|
|||||||
|
|
||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
import org.thingsboard.common.util.ListeningExecutor;
|
import org.thingsboard.common.util.ListeningExecutor;
|
||||||
import org.thingsboard.rule.engine.api.slack.SlackService;
|
import org.thingsboard.rule.engine.api.notification.SlackService;
|
||||||
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
|
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
|
||||||
import org.thingsboard.server.cluster.TbClusterService;
|
import org.thingsboard.server.cluster.TbClusterService;
|
||||||
import org.thingsboard.server.common.data.Customer;
|
import org.thingsboard.server.common.data.Customer;
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 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.rule.engine.api.notification;
|
||||||
|
|
||||||
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
|
||||||
|
public interface FirebaseService {
|
||||||
|
|
||||||
|
void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body);
|
||||||
|
|
||||||
|
}
|
||||||
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.thingsboard.rule.engine.api.slack;
|
package org.thingsboard.rule.engine.api.notification;
|
||||||
|
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
|
import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation;
|
||||||
Loading…
x
Reference in New Issue
Block a user