Browse Source

Added update relation msg functionality

pull/2436/head
Volodymyr Babak 6 years ago
parent
commit
3fc18988be
  1. 90
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  2. 10
      application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
  3. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  4. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java
  5. 182
      application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java
  6. 5
      application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java
  7. 11
      common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java
  8. 2
      common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java
  9. 2
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  10. 3
      dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java
  11. 28
      dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java
  12. 27
      dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java
  13. 12
      dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java
  14. 66
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
  15. 1
      dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java

90
application/src/main/java/org/thingsboard/server/controller/UserController.java

@ -35,13 +35,17 @@ import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId; 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.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
@ -56,6 +60,8 @@ import org.thingsboard.server.utils.MiscUtils;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
@RestController @RestController
@TbCoreComponent @TbCoreComponent
@RequestMapping("/api") @RequestMapping("/api")
@ -300,4 +306,88 @@ public class UserController extends BaseController {
throw handleException(e); throw handleException(e);
} }
} }
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/user/{userId}", method = RequestMethod.POST)
@ResponseBody
public User assignUserToEdge(@PathVariable(EDGE_ID) String strEdgeId,
@PathVariable(USER_ID) String strUserId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(USER_ID, strUserId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
UserId userId = new UserId(toUUID(strUserId));
checkUserId(userId, Operation.ASSIGN_TO_EDGE);
User savedUser = checkNotNull(userService.assignUserToEdge(getTenantId(), userId, edgeId));
logEntityAction(userId, savedUser,
savedUser.getCustomerId(),
ActionType.ASSIGNED_TO_EDGE, null, strUserId, strEdgeId, edge.getName());
return savedUser;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.USER), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strUserId, strEdgeId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/user/{userId}", method = RequestMethod.DELETE)
@ResponseBody
public User unassignUserFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
@PathVariable(USER_ID) String strUserId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(USER_ID, strUserId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
UserId userId = new UserId(toUUID(strUserId));
User user = checkUserId(userId, Operation.UNASSIGN_FROM_EDGE);
User savedUser = checkNotNull(userService.unassignUserFromEdge(getTenantId(), userId, edgeId));
logEntityAction(userId, savedUser,
savedUser.getCustomerId(),
ActionType.UNASSIGNED_FROM_EDGE, null, strUserId, edge.getId().toString(), edge.getName());
return savedUser;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.USER), null,
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strUserId);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/users", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TimePageData<User> getEdgeUsers(
@PathVariable(EDGE_ID) String strEdgeId,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ);
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(userService.findUsersByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
} catch (Exception e) {
throw handleException(e);
}
}
} }

10
application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -16,6 +16,7 @@
package org.thingsboard.server.service.edge; package org.thingsboard.server.service.edge;
import lombok.Data; import lombok.Data;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -37,9 +38,10 @@ import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgCon
import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.UserUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.UserUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor; import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.state.DeviceStateService;
@ -138,4 +140,8 @@ public class EdgeContextComponent {
@Lazy @Lazy
@Autowired @Autowired
private EdgeEventStorageSettings edgeEventStorageSettings; private EdgeEventStorageSettings edgeEventStorageSettings;
@Autowired
@Getter
private DbCallbackExecutorService dbCallbackExecutor;
} }

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -141,7 +141,7 @@ public final class EdgeGrpcSession implements Closeable {
outputStream.onError(new RuntimeException(responseMsg.getErrorMsg())); outputStream.onError(new RuntimeException(responseMsg.getErrorMsg()));
} }
if (ConnectResponseCode.ACCEPTED == responseMsg.getResponseCode()) { if (ConnectResponseCode.ACCEPTED == responseMsg.getResponseCode()) {
ctx.getSyncEdgeService().sync(edge, outputStream); ctx.getSyncEdgeService().sync(ctx, edge, outputStream);
} }
} }
if (connected) { if (connected) {

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,

182
application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -17,7 +17,6 @@ package org.thingsboard.server.service.edge.rpc.init;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import io.grpc.stub.StreamObserver; import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -26,7 +25,7 @@ import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -45,6 +44,7 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.gen.edge.AssetUpdateMsg; import org.thingsboard.server.gen.edge.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
@ -56,12 +56,15 @@ import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType; import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.UserUpdateMsg;
import org.thingsboard.server.service.edge.EdgeContextComponent;
import org.thingsboard.server.service.edge.rpc.constructor.AssetUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.AssetUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.UserUpdateMsgConstructor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
@ -92,10 +95,10 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
private DashboardService dashboardService; private DashboardService dashboardService;
@Autowired @Autowired
private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor; private UserService userService;
@Autowired @Autowired
private RelationUpdateMsgConstructor relationUpdateMsgConstructor; private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor;
@Autowired @Autowired
private DeviceUpdateMsgConstructor deviceUpdateMsgConstructor; private DeviceUpdateMsgConstructor deviceUpdateMsgConstructor;
@ -109,68 +112,56 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
@Autowired @Autowired
private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor; private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor;
@Autowired
private UserUpdateMsgConstructor userUpdateMsgConstructor;
@Autowired
private RelationUpdateMsgConstructor relationUpdateMsgConstructor;
@Override @Override
public void sync(Edge edge, StreamObserver<ResponseMsg> outputStream) { public void sync(EdgeContextComponent ctx, Edge edge, StreamObserver<ResponseMsg> outputStream) {
Set<EntityId> pushedEntityIds = new HashSet<>(); Set<EntityId> pushedEntityIds = new HashSet<>();
syncRuleChains(edge, pushedEntityIds, outputStream); syncRuleChains(edge, pushedEntityIds, outputStream);
syncDevices(edge, pushedEntityIds, outputStream); syncDevices(edge, pushedEntityIds, outputStream);
syncAssets(edge, pushedEntityIds, outputStream); syncAssets(edge, pushedEntityIds, outputStream);
syncEntityViews(edge, pushedEntityIds, outputStream); syncEntityViews(edge, pushedEntityIds, outputStream);
syncDashboards(edge, pushedEntityIds, outputStream); syncDashboards(edge, pushedEntityIds, outputStream);
syncRelations(edge, pushedEntityIds, outputStream); syncUsers(ctx, edge, pushedEntityIds, outputStream);
syncRelations(ctx, edge, pushedEntityIds, outputStream);
} }
private void syncRelations(Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) { private void syncRuleChains(Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
if (!pushedEntityIds.isEmpty()) { try {
List<ListenableFuture<List<EntityRelation>>> futures = new ArrayList<>(); TimePageLink pageLink = new TimePageLink(100);
for (EntityId entityId : pushedEntityIds) { TimePageData<RuleChain> pageData;
futures.add(syncRelations(edge, entityId, EntitySearchDirection.FROM)); do {
futures.add(syncRelations(edge, entityId, EntitySearchDirection.TO)); pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get();
} if (!pageData.getData().isEmpty()) {
ListenableFuture<List<List<EntityRelation>>> relationsListFuture = Futures.allAsList(futures); log.trace("[{}] [{}] rule chains(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
Futures.transform(relationsListFuture, relationsList -> { for (RuleChain ruleChain : pageData.getData()) {
try { RuleChainUpdateMsg ruleChainUpdateMsg =
Set<EntityRelation> uniqueEntityRelations = new HashSet<>(); ruleChainUpdateMsgConstructor.constructRuleChainUpdatedMsg(
if (!relationsList.isEmpty()) { edge.getRootRuleChainId(),
for (List<EntityRelation> entityRelations : relationsList) { UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE,
if (!entityRelations.isEmpty()) { ruleChain);
uniqueEntityRelations.addAll(entityRelations); EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
} .setRuleChainUpdateMsg(ruleChainUpdateMsg)
} .build();
} outputStream.onNext(ResponseMsg.newBuilder()
if (!uniqueEntityRelations.isEmpty()) { .setEntityUpdateMsg(entityUpdateMsg)
log.trace("[{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), uniqueEntityRelations.size()); .build());
for (EntityRelation relation : uniqueEntityRelations) { pushedEntityIds.add(ruleChain.getId());
try {
RelationUpdateMsg relationUpdateMsg =
relationUpdateMsgConstructor.constructRelationUpdatedMsg(
UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE,
relation);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setRelationUpdateMsg(relationUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
} catch (Exception e) {
log.error("Exception during loading relation [{}] to edge on init!", relation, e);
}
}
} }
} catch (Exception e) {
log.error("Exception during loading relation(s) to edge on init!", e);
} }
return null; if (pageData.hasNext()) {
}, MoreExecutors.directExecutor()); pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge rule chain(s) on sync!", e);
} }
} }
private ListenableFuture<List<EntityRelation>> syncRelations(Edge edge, EntityId entityId, EntitySearchDirection direction) {
EntityRelationsQuery query = new EntityRelationsQuery();
query.setParameters(new RelationsSearchParameters(entityId, direction, -1, false));
return relationService.findByQuery(edge.getTenantId(), query);
}
private void syncDevices(Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) { private void syncDevices(Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
try { try {
TimePageLink pageLink = new TimePageLink(100); TimePageLink pageLink = new TimePageLink(100);
@ -198,7 +189,7 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
} }
} while (pageData.hasNext()); } while (pageData.hasNext());
} catch (Exception e) { } catch (Exception e) {
log.error("Exception during loading edge device(s) on init!", e); log.error("Exception during loading edge device(s) on sync!", e);
} }
} }
@ -229,7 +220,7 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
} }
} while (pageData.hasNext()); } while (pageData.hasNext());
} catch (Exception e) { } catch (Exception e) {
log.error("Exception during loading edge asset(s) on init!", e); log.error("Exception during loading edge asset(s) on sync!", e);
} }
} }
@ -260,7 +251,7 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
} }
} while (pageData.hasNext()); } while (pageData.hasNext());
} catch (Exception e) { } catch (Exception e) {
log.error("Exception during loading edge entity view(s) on init!", e); log.error("Exception during loading edge entity view(s) on sync!", e);
} }
} }
@ -292,31 +283,30 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
} }
} while (pageData.hasNext()); } while (pageData.hasNext());
} catch (Exception e) { } catch (Exception e) {
log.error("Exception during loading edge dashboard(s) on init!", e); log.error("Exception during loading edge dashboard(s) on sync!", e);
} }
} }
private void syncRuleChains(Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) { private void syncUsers(EdgeContextComponent ctx, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
try { try {
TimePageLink pageLink = new TimePageLink(100); TimePageLink pageLink = new TimePageLink(100);
TimePageData<RuleChain> pageData; TimePageData<User> pageData;
do { do {
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); pageData = userService.findUsersByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get();
if (!pageData.getData().isEmpty()) { if (!pageData.getData().isEmpty()) {
log.trace("[{}] [{}] rule chains(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); log.trace("[{}] [{}] user(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (RuleChain ruleChain : pageData.getData()) { for (User user : pageData.getData()) {
RuleChainUpdateMsg ruleChainUpdateMsg = UserUpdateMsg userUpdateMsg =
ruleChainUpdateMsgConstructor.constructRuleChainUpdatedMsg( userUpdateMsgConstructor.constructUserUpdatedMsg(
edge.getRootRuleChainId(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE,
UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, user);
ruleChain);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setRuleChainUpdateMsg(ruleChainUpdateMsg) .setUserUpdateMsg(userUpdateMsg)
.build(); .build();
outputStream.onNext(ResponseMsg.newBuilder() outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg) .setEntityUpdateMsg(entityUpdateMsg)
.build()); .build());
pushedEntityIds.add(ruleChain.getId()); pushedEntityIds.add(user.getId());
} }
} }
if (pageData.hasNext()) { if (pageData.hasNext()) {
@ -324,10 +314,62 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
} }
} while (pageData.hasNext()); } while (pageData.hasNext());
} catch (Exception e) { } catch (Exception e) {
log.error("Exception during loading edge rule chain(s) on init!", e); log.error("Exception during loading edge user(s) on sync!", e);
}
}
private void syncRelations(EdgeContextComponent ctx, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
if (!pushedEntityIds.isEmpty()) {
List<ListenableFuture<List<EntityRelation>>> futures = new ArrayList<>();
for (EntityId entityId : pushedEntityIds) {
futures.add(syncRelations(edge, entityId, EntitySearchDirection.FROM));
futures.add(syncRelations(edge, entityId, EntitySearchDirection.TO));
}
ListenableFuture<List<List<EntityRelation>>> relationsListFuture = Futures.allAsList(futures);
Futures.transform(relationsListFuture, relationsList -> {
try {
Set<EntityRelation> uniqueEntityRelations = new HashSet<>();
if (!relationsList.isEmpty()) {
for (List<EntityRelation> entityRelations : relationsList) {
if (!entityRelations.isEmpty()) {
uniqueEntityRelations.addAll(entityRelations);
}
}
}
if (!uniqueEntityRelations.isEmpty()) {
log.trace("[{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), uniqueEntityRelations.size());
for (EntityRelation relation : uniqueEntityRelations) {
try {
RelationUpdateMsg relationUpdateMsg =
relationUpdateMsgConstructor.constructRelationUpdatedMsg(
UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE,
relation);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setRelationUpdateMsg(relationUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
} catch (Exception e) {
log.error("Exception during loading relation [{}] to edge on sync!", relation, e);
}
}
}
} catch (Exception e) {
log.error("Exception during loading relation(s) to edge on sync!", e);
}
return null;
}, ctx.getDbCallbackExecutor());
} }
} }
private ListenableFuture<List<EntityRelation>> syncRelations(Edge edge, EntityId entityId, EntitySearchDirection direction) {
EntityRelationsQuery query = new EntityRelationsQuery();
query.setParameters(new RelationsSearchParameters(entityId, direction, -1, false));
return relationService.findByQuery(edge.getTenantId(), query);
}
@Override @Override
public void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver<ResponseMsg> outputStream) { public void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver<ResponseMsg> outputStream) {
if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) { if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) {

5
application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -19,10 +19,11 @@ import io.grpc.stub.StreamObserver;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.gen.edge.ResponseMsg; import org.thingsboard.server.gen.edge.ResponseMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg; import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg;
import org.thingsboard.server.service.edge.EdgeContextComponent;
public interface SyncEdgeService { public interface SyncEdgeService {
void sync(Edge edge, StreamObserver<ResponseMsg> outputStream); void sync(EdgeContextComponent ctx, Edge edge, StreamObserver<ResponseMsg> outputStream);
void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver<ResponseMsg> outputStream); void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver<ResponseMsg> outputStream);
} }

11
common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java

@ -17,12 +17,17 @@ package org.thingsboard.server.dao.user;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId; 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.TenantId;
import org.thingsboard.server.common.data.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserCredentialsId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
public interface UserService { public interface UserService {
@ -66,4 +71,10 @@ public interface UserService {
void onUserLoginSuccessful(TenantId tenantId, UserId userId); void onUserLoginSuccessful(TenantId tenantId, UserId userId);
int onUserLoginIncorrectCredentials(TenantId tenantId, UserId userId); int onUserLoginIncorrectCredentials(TenantId tenantId, UserId userId);
User assignUserToEdge(TenantId tenantId, UserId userId, EdgeId edgeId);
User unassignUserFromEdge(TenantId tenantId, UserId userId, EdgeId edgeId);
ListenableFuture<TimePageData<User>> findUsersByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
} }

2
common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,

2
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -80,10 +80,8 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
public class BaseAssetService extends AbstractEntityService implements AssetService { public class BaseAssetService extends AbstractEntityService implements AssetService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId "; public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_ASSET_ID = "Incorrect assetId "; public static final String INCORRECT_ASSET_ID = "Incorrect assetId ";
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
@Autowired @Autowired
private AssetDao assetDao; private AssetDao assetDao;

3
dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java

@ -31,6 +31,9 @@ import java.util.concurrent.ExecutionException;
@Slf4j @Slf4j
public abstract class AbstractEntityService { public abstract class AbstractEntityService {
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
@Autowired @Autowired
protected RelationService relationService; protected RelationService relationService;

28
dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java

@ -15,20 +15,31 @@
*/ */
package org.thingsboard.server.dao.sql.user; package org.thingsboard.server.dao.sql.user;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.UserEntity; import org.thingsboard.server.dao.model.sql.UserEntity;
import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
import org.thingsboard.server.dao.user.UserDao; import org.thingsboard.server.dao.user.UserDao;
import org.thingsboard.server.dao.util.SqlDao; import org.thingsboard.server.dao.util.SqlDao;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
@ -41,11 +52,15 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR;
*/ */
@Component @Component
@SqlDao @SqlDao
@Slf4j
public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> implements UserDao { public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> implements UserDao {
@Autowired @Autowired
private UserRepository userRepository; private UserRepository userRepository;
@Autowired
private RelationDao relationDao;
@Override @Override
protected Class<UserEntity> getEntityClass() { protected Class<UserEntity> getEntityClass() {
return UserEntity.class; return UserEntity.class;
@ -87,4 +102,17 @@ public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> imple
PageRequest.of(0, pageLink.getLimit()))); PageRequest.of(0, pageLink.getLimit())));
} }
@Override
public ListenableFuture<List<User>> findUsersByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
log.debug("Try to find users by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.USER, pageLink);
return Futures.transformAsync(relations, input -> {
List<ListenableFuture<User>> userFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
userFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
}
return Futures.successfulAsList(userFutures);
}, MoreExecutors.directExecutor());
}
} }

27
dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java

@ -16,18 +16,30 @@
package org.thingsboard.server.dao.user; package org.thingsboard.server.dao.user;
import com.datastax.driver.core.querybuilder.Select.Where; import com.datastax.driver.core.querybuilder.Select.Where;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.nosql.UserEntity; import org.thingsboard.server.dao.model.nosql.UserEntity;
import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.util.NoSqlDao; import org.thingsboard.server.dao.util.NoSqlDao;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -40,6 +52,9 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
@NoSqlDao @NoSqlDao
public class CassandraUserDao extends CassandraAbstractSearchTextDao<UserEntity, User> implements UserDao { public class CassandraUserDao extends CassandraAbstractSearchTextDao<UserEntity, User> implements UserDao {
@Autowired
private RelationDao relationDao;
@Override @Override
protected Class<UserEntity> getColumnFamilyClass() { protected Class<UserEntity> getColumnFamilyClass() {
return UserEntity.class; return UserEntity.class;
@ -86,4 +101,16 @@ public class CassandraUserDao extends CassandraAbstractSearchTextDao<UserEntity,
return DaoUtil.convertDataList(userEntities); return DaoUtil.convertDataList(userEntities);
} }
@Override
public ListenableFuture<List<User>> findUsersByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
log.debug("Try to find users by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.USER, pageLink);
return Futures.transformAsync(relations, input -> {
List<ListenableFuture<User>> userFutures = new ArrayList<>(input.size());
for (EntityRelation relation : input) {
userFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
}
return Futures.successfulAsList(userFutures);
}, MoreExecutors.directExecutor());
}
} }

12
dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java

@ -15,9 +15,11 @@
*/ */
package org.thingsboard.server.dao.user; package org.thingsboard.server.dao.user;
import com.google.common.util.concurrent.ListenableFuture;
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.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
import java.util.List; import java.util.List;
@ -59,5 +61,15 @@ public interface UserDao extends Dao<User> {
* @return the list of user entities * @return the list of user entities
*/ */
List<User> findCustomerUsers(UUID tenantId, UUID customerId, TextPageLink pageLink); List<User> findCustomerUsers(UUID tenantId, UUID customerId, TextPageLink pageLink);
/**
* Find users by tenantId, edgeId and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param pageLink the page link
* @return the list of user objects
*/
ListenableFuture<List<User>> findUsersByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
} }

66
dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

@ -18,7 +18,10 @@ package org.thingsboard.server.dao.user;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -28,15 +31,22 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId; 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.TenantId;
import org.thingsboard.server.common.data.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserCredentialsId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
@ -45,9 +55,11 @@ import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.tenant.TenantDao;
import javax.annotation.Nullable;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutionException;
import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validatePageLink; import static org.thingsboard.server.dao.service.Validator.validatePageLink;
@ -85,6 +97,9 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
@Autowired @Autowired
private CustomerDao customerDao; private CustomerDao customerDao;
@Autowired
private EdgeService edgeService;
@Override @Override
public User findUserByEmail(TenantId tenantId, String email) { public User findUserByEmail(TenantId tenantId, String email) {
log.trace("Executing findUserByEmail [{}]", email); log.trace("Executing findUserByEmail [{}]", email);
@ -312,6 +327,57 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
return failedLoginAttempts; return failedLoginAttempts;
} }
@Override
public User assignUserToEdge(TenantId tenantId, UserId userId, EdgeId edgeId) {
User user = findUserById(tenantId, userId);
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't assign user to non-existent edge!");
}
if (!edge.getTenantId().getId().equals(user.getTenantId().getId())) {
throw new DataValidationException("Can't assign user to edge from different tenant!");
}
try {
createRelation(tenantId, new EntityRelation(edgeId, userId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create user relation. Edge Id: [{}]", userId, edgeId);
throw new RuntimeException(e);
}
return user;
}
@Override
public User unassignUserFromEdge(TenantId tenantId, UserId userId, EdgeId edgeId) {
User user = findUserById(tenantId, userId);
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't unassign user from non-existent edge!");
}
try {
deleteRelation(tenantId, new EntityRelation(edgeId, userId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete user relation. Edge Id: [{}]", userId, edgeId);
throw new RuntimeException(e);
}
return user;
}
@Override
public ListenableFuture<TimePageData<User>> findUsersByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
log.trace("Executing findUsersByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
ListenableFuture<List<User>> users = userDao.findUsersByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
return Futures.transform(users, new Function<List<User>, TimePageData<User>>() {
@Nullable
@Override
public TimePageData<User> apply(@Nullable List<User> users) {
return new TimePageData<>(users, pageLink);
}
}, MoreExecutors.directExecutor());
}
private int increaseFailedLoginAttempts(User user) { private int increaseFailedLoginAttempts(User user) {
JsonNode additionalInfo = user.getAdditionalInfo(); JsonNode additionalInfo = user.getAdditionalInfo();
if (!(additionalInfo instanceof ObjectNode)) { if (!(additionalInfo instanceof ObjectNode)) {

1
dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java

@ -331,6 +331,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest {
result = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId).get(); result = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId).get();
Assert.assertEquals(1, result.size()); Assert.assertEquals(1, result.size());
} }
private RuleChainId saveRuleChainAndSetDefaultEdge(String name) { private RuleChainId saveRuleChainAndSetDefaultEdge(String name) {
RuleChain edgeRuleChain = new RuleChain(); RuleChain edgeRuleChain = new RuleChain();
edgeRuleChain.setTenantId(tenantId); edgeRuleChain.setTenantId(tenantId);

Loading…
Cancel
Save