10 changed files with 218 additions and 5 deletions
@ -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; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue