Feature/tenant profile edge sync

This commit is contained in:
Andrii Landiak 2023-08-10 10:38:38 +03:00 committed by GitHub
parent 0817108fac
commit 9e1ab9abae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 699 additions and 39 deletions

View File

@ -49,6 +49,8 @@ import org.thingsboard.server.service.edge.rpc.processor.ota.OtaPackageEdgeProce
import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantProfileEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.user.UserEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetBundleEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetTypeEdgeProcessor;
@ -111,6 +113,12 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
@Autowired
private QueueEdgeProcessor queueProcessor;
@Autowired
private TenantEdgeProcessor tenantEdgeProcessor;
@Autowired
private TenantProfileEdgeProcessor tenantProfileEdgeProcessor;
@Autowired
private AlarmEdgeProcessor alarmProcessor;
@ -201,6 +209,12 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
case RELATION:
future = relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg);
break;
case TENANT:
future = tenantEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break;
case TENANT_PROFILE:
future = tenantProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break;
default:
log.warn("Edge event type [{}] is not designed to be pushed to edge", type);
future = Futures.immediateFuture(null);

View File

@ -35,6 +35,8 @@ import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -55,6 +57,8 @@ import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgePr
import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.settings.AdminSettingsEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.telemetry.TelemetryEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantProfileEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.user.UserEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetBundleEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetTypeEdgeProcessor;
@ -122,6 +126,12 @@ public class EdgeContextComponent {
@Autowired
private OtaPackageService otaPackageService;
@Autowired
private TenantService tenantService;
@Autowired
private TenantProfileService tenantProfileService;
@Autowired
private QueueService queueService;
@ -179,6 +189,12 @@ public class EdgeContextComponent {
@Autowired
private QueueEdgeProcessor queueEdgeProcessor;
@Autowired
private TenantEdgeProcessor tenantEdgeProcessor;
@Autowired
private TenantProfileEdgeProcessor tenantProfileEdgeProcessor;
@Autowired
private EdgeMsgConstructor edgeMsgConstructor;

View File

@ -640,6 +640,10 @@ public final class EdgeGrpcSession implements Closeable {
return ctx.getOtaPackageEdgeProcessor().convertOtaPackageEventToDownlink(edgeEvent);
case QUEUE:
return ctx.getQueueEdgeProcessor().convertQueueEventToDownlink(edgeEvent);
case TENANT:
return ctx.getTenantEdgeProcessor().convertTenantEventToDownlink(edgeEvent);
case TENANT_PROFILE:
return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent);
default:
log.warn("Unsupported edge event type [{}]", edgeEvent);
return null;

View File

@ -34,6 +34,7 @@ import org.thingsboard.server.service.edge.rpc.fetch.QueuesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetsBundlesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantAdminUsersEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetsBundlesEdgeEventFetcher;
import java.util.LinkedList;
@ -53,6 +54,7 @@ public class EdgeSyncCursor {
fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService(), ctx.getFreemarkerConfig()));
fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService()));
fetchers.add(new AssetProfilesEdgeEventFetcher(ctx.getAssetProfileService()));
fetchers.add(new TenantEdgeEventFetcher(ctx.getTenantService()));
fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService()));
Customer publicCustomer = ctx.getCustomerService().findOrCreatePublicCustomer(edge.getTenantId());
fetchers.add(new CustomerEdgeEventFetcher(publicCustomer.getId()));

View File

@ -0,0 +1,67 @@
/**
* 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.edge.rpc.constructor;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@TbCoreComponent
public class TenantMsgConstructor {
public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) {
TenantUpdateMsg.Builder builder = TenantUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(tenant.getId().getId().getMostSignificantBits())
.setIdLSB(tenant.getId().getId().getLeastSignificantBits())
.setTitle(tenant.getTitle())
.setProfileIdMSB(tenant.getTenantProfileId().getId().getMostSignificantBits())
.setProfileIdLSB(tenant.getTenantProfileId().getId().getLeastSignificantBits())
.setRegion(tenant.getRegion());
if (tenant.getCountry() != null) {
builder.setCountry(tenant.getCountry());
}
if (tenant.getState() != null) {
builder.setState(tenant.getState());
}
if (tenant.getCity() != null) {
builder.setCity(tenant.getCity());
}
if (tenant.getAddress() != null) {
builder.setAddress(tenant.getAddress());
}
if (tenant.getAddress2() != null) {
builder.setAddress2(tenant.getAddress2());
}
if (tenant.getZip() != null) {
builder.setZip(tenant.getZip());
}
if (tenant.getPhone() != null) {
builder.setPhone(tenant.getPhone());
}
if (tenant.getEmail() != null) {
builder.setEmail(tenant.getEmail());
}
if (tenant.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(tenant.getAdditionalInfo()));
}
return builder.build();
}
}

View File

@ -0,0 +1,48 @@
/**
* 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.edge.rpc.constructor;
import com.google.protobuf.ByteString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@TbCoreComponent
public class TenantProfileMsgConstructor {
@Autowired
private DataDecodingEncodingService dataDecodingEncodingService;
public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile) {
TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(tenantProfile.getId().getId().getMostSignificantBits())
.setIdLSB(tenantProfile.getId().getId().getLeastSignificantBits())
.setName(tenantProfile.getName())
.setDefault(tenantProfile.isDefault())
.setIsolatedRuleChain(tenantProfile.isIsolatedTbRuleEngine())
.setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())));
if (tenantProfile.getDescription() != null) {
builder.setDescription(tenantProfile.getDescription());
}
return builder.build();
}
}

View File

@ -0,0 +1,51 @@
/**
* 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.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.tenant.TenantService;
import java.util.List;
@AllArgsConstructor
@Slf4j
public class TenantEdgeEventFetcher extends BasePageableEdgeEventFetcher<Tenant> {
private final TenantService tenantService;
@Override
PageData<Tenant> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
Tenant tenant = tenantService.findTenantById(tenantId);
// returns PageData object to be in sync with other fetchers
return new PageData<>(List.of(tenant), 1, 1, false);
}
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, Tenant entity) {
return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.TENANT,
EdgeEventActionType.UPDATED, entity.getId(), null);
}
}

View File

@ -63,6 +63,7 @@ import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
@ -86,6 +87,8 @@ import org.thingsboard.server.service.edge.rpc.constructor.OtaPackageMsgConstruc
import org.thingsboard.server.service.edge.rpc.constructor.QueueMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.TenantMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.TenantProfileMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor;
@ -142,6 +145,9 @@ public abstract class BaseEdgeProcessor {
@Autowired
protected TenantService tenantService;
@Autowired
protected TenantProfileService tenantProfileService;
@Autowired
protected EdgeService edgeService;
@ -236,6 +242,12 @@ public abstract class BaseEdgeProcessor {
@Autowired
protected AssetProfileMsgConstructor assetProfileMsgConstructor;
@Autowired
protected TenantMsgConstructor tenantMsgConstructor;
@Autowired
protected TenantProfileMsgConstructor tenantProfileMsgConstructor;
@Autowired
protected WidgetsBundleMsgConstructor widgetsBundleMsgConstructor;

View File

@ -0,0 +1,57 @@
/**
* 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.edge.rpc.processor.tenant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@Component
@Slf4j
@TbCoreComponent
public class TenantEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg convertTenantEventToDownlink(EdgeEvent edgeEvent) {
TenantId tenantId = new TenantId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) {
Tenant tenant = tenantService.findTenantById(tenantId);
if (tenant != null) {
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction());
TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructor.constructTenantUpdateMsg(msgType, tenant);
TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId());
TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addTenantUpdateMsg(tenantUpdateMsg)
.addTenantProfileUpdateMsg(tenantProfileUpdateMsg)
.build();
}
}
return downlinkMsg;
}
}

View File

@ -0,0 +1,53 @@
/**
* 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.edge.rpc.processor.tenant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@Component
@Slf4j
@TbCoreComponent
public class TenantProfileEdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg convertTenantProfileEventToDownlink(EdgeEvent edgeEvent) {
TenantProfileId tenantProfileId = new TenantProfileId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) {
TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(edgeEvent.getTenantId(), tenantProfileId);
if (tenantProfile != null) {
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction());
TenantProfileUpdateMsg tenantProfileUpdateMsg =
tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addTenantProfileUpdateMsg(tenantProfileUpdateMsg)
.build();
}
}
return downlinkMsg;
}
}

View File

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
public interface TbTenantProfileService {
TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile) throws ThingsboardException;
void delete(TenantId tenantId, TenantProfile tenantProfile) throws ThingsboardException;

View File

@ -46,6 +46,8 @@ import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
@ -63,6 +65,8 @@ import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
@ -70,6 +74,7 @@ import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
@ -855,7 +860,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
EdgeImitator edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret());
edgeImitator.ignoreType(UserCredentialsUpdateMsg.class);
edgeImitator.expectMessageAmount(21);
edgeImitator.expectMessageAmount(23);
edgeImitator.connect();
assertThat(edgeImitator.waitForMessages()).as("await for messages on first connect").isTrue();
@ -868,7 +873,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty());
edgeImitator.expectMessageAmount(16);
edgeImitator.expectMessageAmount(18);
doPost("/api/edge/sync/" + edge.getId());
assertThat(edgeImitator.waitForMessages()).as("await for messages after edge sync rest api call").isTrue();
@ -905,6 +910,8 @@ public class EdgeControllerTest extends AbstractControllerTest {
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "test"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgs(), tenantId));
Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgs(), tenantProfileId));
Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgs()));
}
@ -1036,6 +1043,36 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
private boolean popTenantMsg(List<AbstractMessage> messages, TenantId tenantId1) {
for (AbstractMessage message : messages) {
if (message instanceof TenantUpdateMsg) {
TenantUpdateMsg tenantUpdateMsg = (TenantUpdateMsg) message;
TenantId tenantIdMsg = new TenantId(new UUID(tenantUpdateMsg.getIdMSB(), tenantUpdateMsg.getIdLSB()));
if (UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(tenantUpdateMsg.getMsgType())
&& tenantId1.equals(tenantIdMsg)) {
messages.remove(message);
return true;
}
}
}
return false;
}
private boolean popTenantProfileMsg(List<AbstractMessage> messages, TenantProfileId tenantProfileId) {
for (AbstractMessage message : messages) {
if (message instanceof TenantProfileUpdateMsg) {
TenantProfileUpdateMsg tenantProfileUpdateMsg = (TenantProfileUpdateMsg) message;
TenantProfileId tenantProfileIdMsg = new TenantProfileId(new UUID(tenantProfileUpdateMsg.getIdMSB(), tenantProfileUpdateMsg.getIdLSB()));
if (UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(tenantProfileUpdateMsg.getMsgType())
&& tenantProfileId.equals(tenantProfileIdMsg)) {
messages.remove(message);
return true;
}
}
}
return false;
}
private boolean popSyncCompletedMsg(List<AbstractMessage> messages) {
for (AbstractMessage message : messages) {
if (message instanceof SyncCompletedMsg) {

View File

@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.asset.Asset;
@ -84,6 +85,8 @@ import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
@ -126,7 +129,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
installation();
edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret());
edgeImitator.expectMessageAmount(22);
edgeImitator.expectMessageAmount(24);
edgeImitator.connect();
requestEdgeRuleChainMetadata();
@ -257,6 +260,12 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
// 1 message from user fetcher
validateUsers();
// 1 from tenant fetcher
validateTenant();
// 1 from tenant profile fetcher
validateTenantProfile();
// 1 message sync completed
validateSyncCompleted();
}
@ -267,6 +276,29 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
testAutoGeneratedCodeByProtobuf(configuration);
}
private void validateTenant() throws Exception {
Optional<TenantUpdateMsg> tenantUpdateMsgOpt = edgeImitator.findMessageByType(TenantUpdateMsg.class);
Assert.assertTrue(tenantUpdateMsgOpt.isPresent());
TenantUpdateMsg tenantUpdateMsg = tenantUpdateMsgOpt.get();
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType());
UUID tenantUUID = new UUID(tenantUpdateMsg.getIdMSB(), tenantUpdateMsg.getIdLSB());
Tenant tenant = doGet("/api/tenant/" + tenantUUID, Tenant.class);
Assert.assertNotNull(tenant);
testAutoGeneratedCodeByProtobuf(tenantUpdateMsg);
}
private void validateTenantProfile() throws Exception {
Optional<TenantProfileUpdateMsg> tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class);
Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent());
TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get();
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantProfileUpdateMsg.getMsgType());
UUID tenantProfileUUID = new UUID(tenantProfileUpdateMsg.getIdMSB(), tenantProfileUpdateMsg.getIdLSB());
Tenant tenant = doGet("/api/tenant/" + tenantId.getId(), Tenant.class);
Assert.assertNotNull(tenant);
Assert.assertEquals(tenantProfileUUID, tenant.getTenantProfileId().getId());
testAutoGeneratedCodeByProtobuf(tenantProfileUpdateMsg);
}
private void validateDeviceProfiles() throws Exception {
List<DeviceProfileUpdateMsg> deviceProfileUpdateMsgList = edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class);
// default msg device profile from fetcher

View File

@ -0,0 +1,90 @@
/**
* 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.edge;
import org.junit.Assert;
import org.junit.Test;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import java.util.Optional;
import java.util.UUID;
@DaoSqlTest
public class TenantEdgeTest extends AbstractEdgeTest {
@Test
public void testUpdateTenant() throws Exception {
loginSysAdmin();
// save current value into tmp to revert after test
Tenant savedTenant = doGet("/api/tenant/" + tenantId, Tenant.class);
// updated edge tenant
savedTenant.setTitle("Updated Title for Tenant Edge Test");
edgeImitator.expectMessageAmount(2); // expect tenant and tenant profile update msg
savedTenant = doPost("/api/tenant", savedTenant, Tenant.class);
Assert.assertTrue(edgeImitator.waitForMessages());
Optional<TenantUpdateMsg> tenantUpdateMsgOpt = edgeImitator.findMessageByType(TenantUpdateMsg.class);
Assert.assertTrue(tenantUpdateMsgOpt.isPresent());
TenantUpdateMsg tenantUpdateMsg = tenantUpdateMsgOpt.get();
Optional<TenantProfileUpdateMsg> tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class);
Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent());
TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get();
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType());
Assert.assertEquals(savedTenant.getUuidId().getMostSignificantBits(), tenantUpdateMsg.getIdMSB());
Assert.assertEquals(savedTenant.getUuidId().getLeastSignificantBits(), tenantUpdateMsg.getIdLSB());
Assert.assertEquals(savedTenant.getTitle(), tenantUpdateMsg.getTitle());
Assert.assertEquals("Updated Title for Tenant Edge Test", tenantUpdateMsg.getTitle());
Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId
(new UUID(tenantUpdateMsg.getProfileIdMSB(), tenantUpdateMsg.getProfileIdLSB())));
Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId
(new UUID(tenantProfileUpdateMsg.getIdMSB(), tenantProfileUpdateMsg.getIdLSB())));
//change tenant profile for tenant
TenantProfile tenantProfile = createTenantProfile();
savedTenant.setTenantProfileId(tenantProfile.getId());
edgeImitator.expectMessageAmount(2); // expect tenant and tenant profile update msg
savedTenant = doPost("/api/tenant", savedTenant, Tenant.class);
Assert.assertTrue(edgeImitator.waitForMessages());
tenantUpdateMsgOpt = edgeImitator.findMessageByType(TenantUpdateMsg.class);
Assert.assertTrue(tenantUpdateMsgOpt.isPresent());
tenantUpdateMsg = tenantUpdateMsgOpt.get();
tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class);
Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent());
tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get();
// tenant update
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType());
Assert.assertEquals(savedTenant.getUuidId().getMostSignificantBits(), tenantUpdateMsg.getIdMSB());
Assert.assertEquals(savedTenant.getUuidId().getLeastSignificantBits(), tenantUpdateMsg.getIdLSB());
Assert.assertEquals(savedTenant.getTitle(), tenantUpdateMsg.getTitle());
Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId
(new UUID(tenantUpdateMsg.getProfileIdMSB(), tenantUpdateMsg.getProfileIdLSB())));
Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId
(new UUID(tenantProfileUpdateMsg.getIdMSB(), tenantProfileUpdateMsg.getIdLSB())));
}
private TenantProfile createTenantProfile() {
TenantProfile tenantProfile = new TenantProfile();
tenantProfile.setName("TestEdge tenant profile");
return doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
}
}

View File

@ -0,0 +1,54 @@
/**
* 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.edge;
import com.google.protobuf.AbstractMessage;
import org.junit.Assert;
import org.junit.Test;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
@DaoSqlTest
public class TenantProfileEdgeTest extends AbstractEdgeTest {
@Test
public void testTenantProfiles() throws Exception {
loginSysAdmin();
// save current values into tmp to revert after test
TenantProfile edgeTenantProfile = doGet("/api/tenantProfile/" + tenantProfileId.getId(), TenantProfile.class);
// updated edge tenant profile
edgeTenantProfile.setName("Tenant Profile Edge Test");
edgeTenantProfile.setDescription("Updated tenant profile Edge Test");
edgeImitator.expectMessageAmount(1);
edgeTenantProfile = doPost("/api/tenantProfile", edgeTenantProfile, TenantProfile.class);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof TenantProfileUpdateMsg);
TenantProfileUpdateMsg tenantProfileUpdateMsg = (TenantProfileUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantProfileUpdateMsg.getMsgType());
Assert.assertEquals(edgeTenantProfile.getUuidId().getMostSignificantBits(), tenantProfileUpdateMsg.getIdMSB());
Assert.assertEquals(edgeTenantProfile.getUuidId().getLeastSignificantBits(), tenantProfileUpdateMsg.getIdLSB());
Assert.assertEquals(edgeTenantProfile.getDescription(), tenantProfileUpdateMsg.getDescription());
Assert.assertEquals("Updated tenant profile Edge Test", tenantProfileUpdateMsg.getDescription());
Assert.assertEquals("Tenant Profile Edge Test", tenantProfileUpdateMsg.getName());
loginTenantAdmin();
}
}

View File

@ -47,6 +47,8 @@ import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
@ -290,6 +292,16 @@ public class EdgeImitator {
result.add(saveDownlinkMsg(queueUpdateMsg));
}
}
if (downlinkMsg.getTenantUpdateMsgCount() > 0) {
for (TenantUpdateMsg tenantUpdateMsg : downlinkMsg.getTenantUpdateMsgList()) {
result.add(saveDownlinkMsg(tenantUpdateMsg));
}
}
if (downlinkMsg.getTenantProfileUpdateMsgCount() > 0) {
for (TenantProfileUpdateMsg tenantProfileUpdateMsg : downlinkMsg.getTenantProfileUpdateMsgList()) {
result.add(saveDownlinkMsg(tenantProfileUpdateMsg));
}
}
if (downlinkMsg.hasEdgeConfiguration()) {
result.add(saveDownlinkMsg(downlinkMsg.getEdgeConfiguration()));
}

View File

@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.EntityDaoService;
@ -83,6 +84,8 @@ public interface EdgeService extends EntityDaoService {
PageData<Edge> findEdgesByTenantIdAndEntityId(TenantId tenantId, EntityId ruleChainId, PageLink pageLink);
PageData<Edge> findEdgesByTenantProfileId(TenantProfileId tenantProfileId, PageLink pageLink);
List<EdgeId> findAllRelatedEdgeIds(TenantId tenantId, EntityId entityId);
PageData<EdgeId> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink);

View File

@ -69,6 +69,8 @@ public final class EdgeUtils {
return EdgeEventType.OTA_PACKAGE;
case QUEUE:
return EdgeEventType.QUEUE;
case TENANT_PROFILE:
return EdgeEventType.TENANT_PROFILE;
default:
log.warn("Unsupported entity type [{}]", entityType);
return null;

View File

@ -27,6 +27,8 @@ import java.util.List;
@Data
public class DeviceProfileData implements Serializable {
private static final long serialVersionUID = -3864805547939495272L;
@ApiModelProperty(position = 1, value = "JSON object of device profile configuration")
private DeviceProfileConfiguration configuration;
@Valid

View File

@ -33,6 +33,7 @@ public enum EdgeEventType {
CUSTOMER(true),
RELATION(true),
TENANT(true),
TENANT_PROFILE(true),
WIDGETS_BUNDLE(true),
WIDGET_TYPE(true),
ADMIN_SETTINGS(true),

View File

@ -115,10 +115,6 @@ public class EntityIdFactory {
return new DashboardId(uuid);
case DEVICE:
return new DeviceId(uuid);
case DEVICE_PROFILE:
return new DeviceProfileId(uuid);
case ASSET_PROFILE:
return new AssetProfileId(uuid);
case ASSET:
return new AssetId(uuid);
case ALARM:
@ -131,12 +127,18 @@ public class EntityIdFactory {
return new WidgetsBundleId(uuid);
case WIDGET_TYPE:
return new WidgetTypeId(uuid);
case DEVICE_PROFILE:
return new DeviceProfileId(uuid);
case ASSET_PROFILE:
return new AssetProfileId(uuid);
case TENANT_PROFILE:
return new TenantProfileId(uuid);
case OTA_PACKAGE:
return new OtaPackageId(uuid);
case QUEUE:
return new QueueId(uuid);
case EDGE:
return new EdgeId(uuid);
case QUEUE:
return new QueueId(uuid);
}
throw new IllegalArgumentException("EdgeEventType " + edgeEventType + " is not supported!");
}

View File

@ -29,6 +29,8 @@ import org.thingsboard.server.common.data.TenantProfileType;
@Data
public class DefaultTenantProfileConfiguration implements TenantProfileConfiguration {
private static final long serialVersionUID = -7134932690332578595L;
private long maxDevices;
private long maxAssets;
private long maxCustomers;

View File

@ -22,6 +22,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.TenantProfileType;
import java.io.Serializable;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
@ -29,7 +31,7 @@ import org.thingsboard.server.common.data.TenantProfileType;
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = DefaultTenantProfileConfiguration.class, name = "DEFAULT")})
public interface TenantProfileConfiguration {
public interface TenantProfileConfiguration extends Serializable {
@JsonIgnore
TenantProfileType getType();

View File

@ -19,11 +19,14 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@ApiModel
@Data
public class TenantProfileData {
public class TenantProfileData implements Serializable {
private static final long serialVersionUID = -3642550257035920976L;
@ApiModelProperty(position = 1, value = "Complex JSON object that contains profile settings: max devices, max assets, rate limits, etc.")
private TenantProfileConfiguration configuration;

View File

@ -20,8 +20,13 @@ import lombok.Data;
import org.thingsboard.server.common.data.queue.ProcessingStrategy;
import org.thingsboard.server.common.data.queue.SubmitStrategy;
import java.io.Serializable;
@Data
public class TenantProfileQueueConfiguration {
public class TenantProfileQueueConfiguration implements Serializable {
private static final long serialVersionUID = -546600745123197362L;
private String name;
private String topic;
private int pollInterval;

View File

@ -383,6 +383,36 @@ message UserCredentialsUpdateMsg {
string password = 4;
}
message TenantUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string title = 4;
int64 profileIdMSB = 5;
int64 profileIdLSB = 6;
string region = 7;
optional string country = 8;
optional string state = 9;
optional string city = 10;
optional string address = 11;
optional string address2 = 12;
optional string zip = 13;
optional string phone = 14;
optional string email = 15;
optional string additionalInfo = 16;
}
message TenantProfileUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string name = 4;
optional string description = 5;
bool default = 6;
bool isolatedRuleChain = 7;
bytes profileDataBytes = 8;
}
message RuleChainMetadataRequestMsg {
int64 ruleChainIdMSB = 1;
int64 ruleChainIdLSB = 2;
@ -569,5 +599,7 @@ message DownlinkMsg {
repeated QueueUpdateMsg queueUpdateMsg = 23;
repeated AssetProfileUpdateMsg assetProfileUpdateMsg = 24;
EdgeConfiguration edgeConfiguration = 25;
repeated TenantUpdateMsg tenantUpdateMsg = 26;
repeated TenantProfileUpdateMsg tenantProfileUpdateMsg = 27;
}

View File

@ -170,4 +170,12 @@ public interface EdgeDao extends Dao<Edge> {
*/
PageData<Edge> findEdgesByTenantIdAndEntityId(UUID tenantId, UUID entityId, EntityType entityType, PageLink pageLink);
}
/**
* Find edges by tenantProfileId.
*
* @param tenantProfileId the tenantProfileId
* @return the list of edge objects
*/
PageData<Edge> findEdgesByTenantProfileId(UUID tenantProfileId, PageLink pageLink);
}

View File

@ -33,6 +33,7 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
@ -45,8 +46,10 @@ import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.IdBased;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
@ -61,6 +64,7 @@ import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
import javax.annotation.Nullable;
@ -100,6 +104,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
@Autowired
private RelationService relationService;
@Autowired
private TenantService tenantService;
@Autowired
private DataValidator<Edge> edgeValidator;
@ -385,6 +392,14 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
return edgeDao.findEdgesByTenantIdAndEntityId(tenantId.getId(), entityId.getId(), entityId.getEntityType(), pageLink);
}
@Override
public PageData<Edge> findEdgesByTenantProfileId(TenantProfileId tenantProfileId, PageLink pageLink) {
log.trace("Executing findEdgesByTenantProfileId, tenantProfileId [{}], pageLink [{}]", tenantProfileId, pageLink);
Validator.validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantProfileId(tenantProfileId.getId(), pageLink);
}
private PaginatedRemover<TenantId, Edge> tenantEdgesRemover =
new PaginatedRemover<TenantId, Edge>() {
@ -459,6 +474,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
} else {
return convertToEdgeIds(findEdgesByTenantIdAndCustomerId(tenantId, userById.getCustomerId(), pageLink));
}
case TENANT_PROFILE:
return convertToEdgeIds(findEdgesByTenantProfileId(new TenantProfileId(entityId.getId()), pageLink));
default:
log.warn("[{}] Unsupported entity type {}", tenantId, entityId.getEntityType());
return createEmptyEdgeIdPageData();

View File

@ -120,6 +120,10 @@ public interface EdgeRepository extends JpaRepository<EdgeEntity, UUID> {
@Param("searchText") String searchText,
Pageable pageable);
@Query("SELECT ee FROM EdgeEntity ee, TenantEntity te WHERE ee.tenantId = te.id AND te.tenantProfileId = :tenantProfileId ")
Page<EdgeEntity> findByTenantProfileId(@Param("tenantProfileId") UUID tenantProfileId,
Pageable pageable);
@Query("SELECT DISTINCT d.type FROM EdgeEntity d WHERE d.tenantId = :tenantId")
List<String> findTenantEdgeTypes(@Param("tenantId") UUID tenantId);

View File

@ -184,6 +184,15 @@ public class JpaEdgeDao extends JpaAbstractDao<EdgeEntity, Edge> implements Edge
DaoUtil.toPageable(pageLink)));
}
@Override
public PageData<Edge> findEdgesByTenantProfileId(UUID tenantProfileId, PageLink pageLink) {
log.debug("Try to find edges by tenantProfileId [{}], pageLink [{}]", tenantProfileId, pageLink);
return DaoUtil.toPageData(
edgeRepository.findByTenantProfileId(
tenantProfileId,
DaoUtil.toPageable(pageLink)));
}
private List<EntitySubtype> convertTenantEdgeTypesToDto(UUID tenantId, List<String> types) {
List<EntitySubtype> list = Collections.emptyList();
if (types != null && !types.isEmpty()) {

View File

@ -32,6 +32,8 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
@ -92,6 +94,8 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService<Tenant
try {
savedTenantProfile = tenantProfileDao.save(tenantId, tenantProfile);
publishEvictEvent(new TenantProfileEvictEvent(savedTenantProfile.getId(), savedTenantProfile.isDefault()));
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId)
.entityId(savedTenantProfile.getId()).added(tenantProfile.getId() == null).build());
} catch (Exception t) {
handleEvictEvent(new TenantProfileEvictEvent(null, tenantProfile.isDefault()));
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
@ -128,6 +132,7 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService<Tenant
}
deleteEntityRelations(tenantId, tenantProfileId);
publishEvictEvent(new TenantProfileEvictEvent(tenantProfileId, isDefault));
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(tenantProfileId).build());
}
@Override

View File

@ -40,6 +40,8 @@ import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.notification.NotificationRequestService;
import org.thingsboard.server.dao.notification.NotificationRuleService;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
@ -191,6 +193,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
boolean create = tenant.getId() == null;
Tenant savedTenant = tenantDao.save(tenant.getId(), tenant);
publishEvictEvent(new TenantEvictEvent(savedTenant.getId(), create));
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(TenantId.SYS_TENANT_ID).entityId(savedTenant.getId()).added(create).build());
if (tenant.getId() == null) {
deviceProfileService.createDefaultDeviceProfile(savedTenant.getId());
assetProfileService.createDefaultAssetProfile(savedTenant.getId());
@ -236,6 +239,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
adminSettingsService.deleteAdminSettingsByTenantId(tenantId);
tenantDao.removeById(tenantId, tenantId.getId());
publishEvictEvent(new TenantEvictEvent(tenantId, true));
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(TenantId.SYS_TENANT_ID).entityId(tenantId).build());
deleteEntityRelations(tenantId, tenantId);
}

View File

@ -141,7 +141,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
}
eventPublisher.publishEvent(SaveEntityEvent.builder()
.tenantId(tenantId == null ? TenantId.SYS_TENANT_ID : tenantId)
.entity(user)
.entity(savedUser)
.entityId(savedUser.getId())
.added(user.getId() == null).build());
return savedUser;

View File

@ -59,6 +59,7 @@ import java.nio.ByteBuffer;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import static org.junit.Assert.assertNotNull;
@ -80,7 +81,7 @@ public abstract class AbstractServiceTest {
@Before
public void beforeAbstractService() {
tenantId = createTenant();
tenantId = createTenant().getId();
}
@After
@ -107,29 +108,10 @@ public abstract class AbstractServiceTest {
.data(JacksonUtil.toString(readFromResource("TestJsonData.json")))
.build();
}
//
// private ComponentDescriptor getOrCreateDescriptor(ComponentScope scope, ComponentType type, String clazz, String configurationDescriptorResource) throws IOException {
// return getOrCreateDescriptor(scope, type, clazz, configurationDescriptorResource, null);
// }
//
// private ComponentDescriptor getOrCreateDescriptor(ComponentScope scope, ComponentType type, String clazz, String configurationDescriptorResource, String actions) throws IOException {
// ComponentDescriptor descriptor = componentDescriptorService.findByClazz(clazz);
// if (descriptor == null) {
// descriptor = new ComponentDescriptor();
// descriptor.setName("test");
// descriptor.setClazz(clazz);
// descriptor.setScope(scope);
// descriptor.setType(type);
// descriptor.setActions(actions);
// descriptor.setConfigurationDescriptor(readFromResource(configurationDescriptorResource));
// componentDescriptorService.saveComponent(descriptor);
// }
// return descriptor;
// }
public JsonNode readFromResource(String resourceName) throws IOException {
try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(resourceName)){
return JacksonUtil.fromBytes(is.readAllBytes());
return JacksonUtil.fromBytes(Objects.requireNonNull(is).readAllBytes());
}
}
@ -172,12 +154,12 @@ public abstract class AbstractServiceTest {
return assetProfile;
}
public TenantId createTenant() {
public Tenant createTenant() {
Tenant tenant = new Tenant();
tenant.setTitle("My tenant " + UUID.randomUUID());
Tenant savedTenant = tenantService.saveTenant(tenant);
assertNotNull(savedTenant);
return savedTenant.getId();
return savedTenant;
}
protected Edge constructEdge(TenantId tenantId, String name, String type) {

View File

@ -78,7 +78,7 @@ public class DeviceServiceTest extends AbstractServiceTest {
@Before
public void before() {
anotherTenantId = createTenant();
anotherTenantId = createTenant().getId();
}
@After

View File

@ -650,4 +650,31 @@ public class EdgeServiceTest extends AbstractServiceTest {
Assert.assertEquals("{\"Rule Chain #3\":[\"Rule Chain #1\",\"Rule Chain #2\"]}", missingToRelatedRuleChains);
}
@Test
public void testFindEdgesByTenantProfileId() {
Tenant tenant1 = createTenant();
Tenant tenant2 = createTenant();
Assert.assertNotNull(tenant1);
Assert.assertNotNull(tenant2);
System.out.println("tenant1" + tenant1);
System.out.println("tenant2" + tenant2);
Edge edge1 = constructEdge(tenant1.getId(), "Tenant1 edge", "default");
Edge edge2 = constructEdge(tenant2.getId(), "Tenant2 edge", "default");
Edge savedEdge1 = edgeService.saveEdge(edge1);
Edge savedEdge2 = edgeService.saveEdge(edge2);
System.out.println("1" + savedEdge1);
System.out.println("2" + savedEdge2);
Assert.assertNotNull(savedEdge1);
Assert.assertNotNull(savedEdge2);
Assert.assertEquals(tenant1.getTenantProfileId(), tenant2.getTenantProfileId());
System.out.println(edgeService.findEdgesByTenantId(tenant1.getId(), new PageLink(1000)).getData());
System.out.println(edgeService.findEdgesByTenantId(tenant2.getId(), new PageLink(1000)).getData());
PageData<Edge> edgesPageData = edgeService.findEdgesByTenantProfileId(tenant2.getTenantProfileId(),
new PageLink(1000));
Assert.assertEquals(2, edgesPageData.getTotalElements());
}
}

View File

@ -204,7 +204,7 @@ public class UserServiceTest extends AbstractServiceTest {
Assert.assertEquals(1, users.size());
Assert.assertEquals(tenantAdminUser, users.get(0));
TenantId secondTenantId = createTenant();
TenantId secondTenantId = createTenant().getId();
List<User> tenantAdmins = new ArrayList<>();
for (int i = 0; i < 124; i++) {