Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@
import org.thingsboard.server.service.cloud.rpc.processor.DeviceProfileCloudProcessor;
import org.thingsboard.server.service.cloud.rpc.processor.EdgeCloudProcessor;
import org.thingsboard.server.service.cloud.rpc.processor.EntityViewCloudProcessor;
import org.thingsboard.server.service.cloud.rpc.processor.MailCloudProcessor;
import org.thingsboard.server.service.cloud.rpc.processor.NotificationCloudProcessor;
import org.thingsboard.server.service.cloud.rpc.processor.OAuth2CloudProcessor;
import org.thingsboard.server.service.cloud.rpc.processor.SendNotificationCloudProcessor;
import org.thingsboard.server.service.cloud.rpc.processor.SmsCloudProcessor;
import org.thingsboard.server.service.cloud.rpc.processor.OtaPackageCloudProcessor;
import org.thingsboard.server.service.cloud.rpc.processor.QueueCloudProcessor;
import org.thingsboard.server.service.cloud.rpc.processor.RelationCloudProcessor;
Expand Down Expand Up @@ -171,6 +174,15 @@ public CloudContextComponent(List<EdgeProcessor> processors) {
@Autowired
private AiModelCloudProcessor aiModelProcessor;

@Autowired
private MailCloudProcessor mailProcessor;

@Autowired
private SmsCloudProcessor smsProcessor;

@Autowired
private SendNotificationCloudProcessor sendNotificationProcessor;

// callback
@Autowired
private DbCallbackExecutorService dbCallbackExecutorService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ public UplinkMsg convertCloudEventToUplink(CloudEvent cloudEvent) {
case RELATION_REQUEST -> cloudCtx.getRelationProcessor().convertRelationRequestEventToUplink(cloudEvent);
case CALCULATED_FIELD_REQUEST -> cloudCtx.getCalculatedFieldProcessor().convertCalculatedFieldRequestEventToUplink(cloudEvent);
case RPC_CALL -> cloudCtx.getDeviceProcessor().convertRpcCallEventToUplink(cloudEvent);
case SEND_EMAIL -> cloudCtx.getMailProcessor().convertSendEmailEventToUplink(cloudEvent);
case SEND_SMS -> cloudCtx.getSmsProcessor().convertSendSmsEventToUplink(cloudEvent);
case SEND_NOTIFICATION -> cloudCtx.getSendNotificationProcessor().convertSendNotificationEventToUplink(cloudEvent);
default -> {
log.warn("Unsupported action type [{}]", cloudEvent);
yield null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright © 2016-2026 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.cloud.rpc.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.cloud.CloudEvent;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.SendEmailUplinkMsg;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;

/**
* Converts a SEND_EMAIL cloud event into a {@link SendEmailUplinkMsg}. The Edge does not render or
* transmit the mail itself; it forwards the serialized {@code EdgeMailRequest} (carried in the cloud
* event body) to the Cloud, which resolves the config, renders and sends via its own SMTP.
*/
@Slf4j
@Component
@TbCoreComponent
public class MailCloudProcessor {

public UplinkMsg convertSendEmailEventToUplink(CloudEvent cloudEvent) {
log.trace("Executing convertSendEmailEventToUplink, cloudEvent [{}]", cloudEvent);
TenantId tenantId = cloudEvent.getTenantId();
SendEmailUplinkMsg sendEmailUplinkMsg = SendEmailUplinkMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setRequest(JacksonUtil.toString(cloudEvent.getEntityBody()))
.build();

return UplinkMsg.newBuilder()
.setUplinkMsgId(EdgeUtils.nextPositiveInt())
.addSendEmailUplinkMsg(sendEmailUplinkMsg)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright © 2016-2026 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.cloud.rpc.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.cloud.CloudEvent;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.SendNotificationUplinkMsg;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;

/**
* Converts a SEND_NOTIFICATION cloud event into a {@link SendNotificationUplinkMsg}. The Edge forwards the
* serialized {@code EdgeNotificationRequest} (carried in the cloud event body) to the Cloud, which resolves
* the channel credentials and delivers (Slack post / FCM push).
*/
@Slf4j
@Component
@TbCoreComponent
public class SendNotificationCloudProcessor {

public UplinkMsg convertSendNotificationEventToUplink(CloudEvent cloudEvent) {
log.trace("Executing convertSendNotificationEventToUplink, cloudEvent [{}]", cloudEvent);
TenantId tenantId = cloudEvent.getTenantId();
SendNotificationUplinkMsg sendNotificationUplinkMsg = SendNotificationUplinkMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setRequest(JacksonUtil.toString(cloudEvent.getEntityBody()))
.build();

return UplinkMsg.newBuilder()
.setUplinkMsgId(EdgeUtils.nextPositiveInt())
.addSendNotificationUplinkMsg(sendNotificationUplinkMsg)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright © 2016-2026 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.cloud.rpc.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.cloud.CloudEvent;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.SendSmsUplinkMsg;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;

/**
* Converts a SEND_SMS cloud event into a {@link SendSmsUplinkMsg}. The Edge does not resolve or transmit
* the SMS itself; it forwards the serialized {@code EdgeSmsRequest} (carried in the cloud event body) to
* the Cloud, which resolves the config and sends via its own provider.
*/
@Slf4j
@Component
@TbCoreComponent
public class SmsCloudProcessor {

public UplinkMsg convertSendSmsEventToUplink(CloudEvent cloudEvent) {
log.trace("Executing convertSendSmsEventToUplink, cloudEvent [{}]", cloudEvent);
TenantId tenantId = cloudEvent.getTenantId();
SendSmsUplinkMsg sendSmsUplinkMsg = SendSmsUplinkMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setRequest(JacksonUtil.toString(cloudEvent.getEntityBody()))
.build();

return UplinkMsg.newBuilder()
.setUplinkMsgId(EdgeUtils.nextPositiveInt())
.addSendSmsUplinkMsg(sendSmsUplinkMsg)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.springframework.context.annotation.Profile;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.edge.EdgeSettings;
import org.thingsboard.server.common.data.id.RuleNodeId;
Expand All @@ -33,17 +34,20 @@
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.cloud.EdgeSettingsService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.component.RuleNodeClassInfo;
import org.thingsboard.server.service.install.DatabaseSchemaSettingsService;
import org.thingsboard.server.service.install.DbUpgradeExecutorService;
import org.thingsboard.server.service.install.SystemDataLoaderService;
import org.thingsboard.server.service.install.lts.LtsMigrationService;
import org.thingsboard.server.utils.TbNodeUpgradeUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;

@Service
Expand All @@ -65,6 +69,8 @@ public class DefaultDataUpdateService implements DataUpdateService {
private final TenantService tenantService;
private final EdgeSettingsService edgeSettingsService;
private final WidgetsBundleService widgetsBundleService;
private final AdminSettingsService adminSettingsService;
private final SystemDataLoaderService systemDataLoaderService;

@Override
public void updateData() throws Exception {
Expand All @@ -79,9 +85,45 @@ public void updateData() throws Exception {

// ... Edge-only

purgeAdminSettings();
log.info("Data updated.");
}

private void purgeAdminSettings() throws Exception {
log.info("Purging admin settings");
Set<String> keep = Set.of("general", "connectivity");
List<TenantId> scopes = new ArrayList<>();
scopes.add(TenantId.SYS_TENANT_ID);
new PageDataIterable<>(tenantService::findTenantsIds, DEFAULT_PAGE_SIZE).forEach(scopes::add);
boolean systemJwtRemoved = false;
for (TenantId scope : scopes) {
List<String> keysToDelete = new ArrayList<>();
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<AdminSettings> page;
do {
page = adminSettingsService.findAllByTenantId(scope, pageLink);
for (AdminSettings adminSettings : page.getData()) {
if (!keep.contains(adminSettings.getKey())) {
keysToDelete.add(adminSettings.getKey());
}
}
pageLink = pageLink.nextPageLink();
} while (page.hasNext());
for (String key : keysToDelete) {
adminSettingsService.deleteAdminSettingsByTenantIdAndKey(scope, key);
if (TenantId.SYS_TENANT_ID.equals(scope) && "jwt".equals(key)) {
systemJwtRemoved = true;
}
}
if (!keysToDelete.isEmpty()) {
log.info("Purged {} admin settings for tenant [{}]: {}", keysToDelete.size(), scope, keysToDelete);
}
}
if (systemJwtRemoved) {
systemDataLoaderService.createRandomJwtSettings();
}
}

@Override
public void upgradeRuleNodes() {
int totalRuleNodesUpgraded = 0;
Expand Down
Loading