diff --git a/application/pom.xml b/application/pom.xml
index f3fd1d04e4..9364cfc16a 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 1.2.3-SNAPSHOT
+ 1.3.0-SNAPSHOT
thingsboard
org.thingsboard
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index 7cc2c9f84f..ae9d9677c7 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.common.transport.auth.DeviceAuthService;
import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
+import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.device.DeviceService;
@@ -80,6 +81,9 @@ public class ActorSystemContext {
@Autowired
@Getter private DeviceService deviceService;
+ @Autowired
+ @Getter private AssetService assetService;
+
@Autowired
@Getter private TenantService tenantService;
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
index 876b52535b..5dbc5f4244 100644
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
@@ -17,7 +17,6 @@ package org.thingsboard.server.actors.plugin;
import java.io.IOException;
import java.util.*;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@@ -30,15 +29,19 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
-import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.Tenant;
+import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.kv.TsKvQuery;
-import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.plugin.PluginMetaData;
+import org.thingsboard.server.common.data.rule.RuleMetaData;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext;
@@ -53,7 +56,6 @@ import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRe
import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
import akka.actor.ActorRef;
-import org.w3c.dom.Attr;
import javax.annotation.Nullable;
@@ -91,103 +93,107 @@ public final class PluginProcessingContext implements PluginContext {
}
@Override
- public void saveAttributes(final TenantId tenantId, final DeviceId deviceId, final String scope, final List attributes, final PluginCallback callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> rsListFuture = pluginCtx.attributesService.save(deviceId, scope, attributes);
+ public void saveAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List attributes, final PluginCallback callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
+ ListenableFuture> rsListFuture = pluginCtx.attributesService.save(entityId, scope, attributes);
Futures.addCallback(rsListFuture, getListCallback(callback, v -> {
- onDeviceAttributesChanged(tenantId, deviceId, scope, attributes);
+ if (entityId.getEntityType() == EntityType.DEVICE) {
+ onDeviceAttributesChanged(tenantId, new DeviceId(entityId.getId()), scope, attributes);
+ }
return null;
}), executor);
}));
}
@Override
- public void removeAttributes(final TenantId tenantId, final DeviceId deviceId, final String scope, final List keys, final PluginCallback callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> future = pluginCtx.attributesService.removeAll(deviceId, scope, keys);
+ public void removeAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List keys, final PluginCallback callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
+ ListenableFuture> future = pluginCtx.attributesService.removeAll(entityId, scope, keys);
Futures.addCallback(future, getCallback(callback, v -> null), executor);
- onDeviceAttributesDeleted(tenantId, deviceId, keys.stream().map(key -> new AttributeKey(scope, key)).collect(Collectors.toSet()));
+ if (entityId.getEntityType() == EntityType.DEVICE) {
+ onDeviceAttributesDeleted(tenantId, new DeviceId(entityId.getId()), keys.stream().map(key -> new AttributeKey(scope, key)).collect(Collectors.toSet()));
+ }
}));
}
@Override
- public void loadAttribute(DeviceId deviceId, String attributeType, String attributeKey, final PluginCallback> callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> future = pluginCtx.attributesService.find(deviceId, attributeType, attributeKey);
+ public void loadAttribute(EntityId entityId, String attributeType, String attributeKey, final PluginCallback> callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
+ ListenableFuture> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKey);
Futures.addCallback(future, getCallback(callback, v -> v), executor);
}));
}
@Override
- public void loadAttributes(DeviceId deviceId, String attributeType, Collection attributeKeys, final PluginCallback> callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> future = pluginCtx.attributesService.find(deviceId, attributeType, attributeKeys);
+ public void loadAttributes(EntityId entityId, String attributeType, Collection attributeKeys, final PluginCallback> callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
+ ListenableFuture> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKeys);
Futures.addCallback(future, getCallback(callback, v -> v), executor);
}));
}
@Override
- public void loadAttributes(DeviceId deviceId, String attributeType, PluginCallback> callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> future = pluginCtx.attributesService.findAll(deviceId, attributeType);
+ public void loadAttributes(EntityId entityId, String attributeType, PluginCallback> callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
+ ListenableFuture> future = pluginCtx.attributesService.findAll(entityId, attributeType);
Futures.addCallback(future, getCallback(callback, v -> v), executor);
}));
}
@Override
- public void loadAttributes(final DeviceId deviceId, final Collection attributeTypes, final PluginCallback> callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
+ public void loadAttributes(final EntityId entityId, final Collection attributeTypes, final PluginCallback> callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
List>> futures = new ArrayList<>();
- attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.findAll(deviceId, attributeType)));
+ attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.findAll(entityId, attributeType)));
convertFuturesAndAddCallback(callback, futures);
}));
}
@Override
- public void loadAttributes(final DeviceId deviceId, final Collection attributeTypes, final Collection attributeKeys, final PluginCallback> callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
+ public void loadAttributes(final EntityId entityId, final Collection attributeTypes, final Collection attributeKeys, final PluginCallback> callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
List>> futures = new ArrayList<>();
- attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.find(deviceId, attributeType, attributeKeys)));
+ attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.find(entityId, attributeType, attributeKeys)));
convertFuturesAndAddCallback(callback, futures);
}));
}
@Override
- public void saveTsData(final DeviceId deviceId, final TsKvEntry entry, final PluginCallback callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> rsListFuture = pluginCtx.tsService.save(DataConstants.DEVICE, deviceId, entry);
+ public void saveTsData(final EntityId entityId, final TsKvEntry entry, final PluginCallback callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
+ ListenableFuture> rsListFuture = pluginCtx.tsService.save(entityId, entry);
Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor);
}));
}
@Override
- public void saveTsData(final DeviceId deviceId, final List entries, final PluginCallback callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> rsListFuture = pluginCtx.tsService.save(DataConstants.DEVICE, deviceId, entries);
+ public void saveTsData(final EntityId entityId, final List entries, final PluginCallback callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
+ ListenableFuture> rsListFuture = pluginCtx.tsService.save(entityId, entries);
Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor);
}));
}
@Override
- public void loadTimeseries(final DeviceId deviceId, final List queries, final PluginCallback> callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> future = pluginCtx.tsService.findAll(DataConstants.DEVICE, deviceId, queries);
+ public void loadTimeseries(final EntityId entityId, final List queries, final PluginCallback> callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
+ ListenableFuture> future = pluginCtx.tsService.findAll(entityId, queries);
Futures.addCallback(future, getCallback(callback, v -> v), executor);
}));
}
@Override
- public void loadLatestTimeseries(final DeviceId deviceId, final PluginCallback> callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
- ResultSetFuture future = pluginCtx.tsService.findAllLatest(DataConstants.DEVICE, deviceId);
+ public void loadLatestTimeseries(final EntityId entityId, final PluginCallback> callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
+ ResultSetFuture future = pluginCtx.tsService.findAllLatest(entityId);
Futures.addCallback(future, getCallback(callback, pluginCtx.tsService::convertResultSetToTsKvEntryList), executor);
}));
}
@Override
- public void loadLatestTimeseries(final DeviceId deviceId, final Collection keys, final PluginCallback> callback) {
- validate(deviceId, new ValidationCallback(callback, ctx -> {
- ListenableFuture> rsListFuture = pluginCtx.tsService.findLatest(DataConstants.DEVICE, deviceId, keys);
+ public void loadLatestTimeseries(final EntityId entityId, final Collection keys, final PluginCallback> callback) {
+ validate(entityId, new ValidationCallback(callback, ctx -> {
+ ListenableFuture> rsListFuture = pluginCtx.tsService.findLatest(entityId, keys);
Futures.addCallback(rsListFuture, getListCallback(callback, rsList ->
{
List result = new ArrayList<>();
@@ -270,24 +276,101 @@ public final class PluginProcessingContext implements PluginContext {
validate(deviceId, new ValidationCallback(callback, ctx -> callback.onSuccess(ctx, null)));
}
- private void validate(DeviceId deviceId, ValidationCallback callback) {
+ private void validate(EntityId entityId, ValidationCallback callback) {
if (securityCtx.isPresent()) {
final PluginApiCallSecurityContext ctx = securityCtx.get();
- if (ctx.isTenantAdmin() || ctx.isCustomerUser()) {
- ListenableFuture deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(deviceId);
- Futures.addCallback(deviceFuture, getCallback(callback, device -> {
- if (device == null) {
- return Boolean.FALSE;
- } else {
- if (!device.getTenantId().equals(ctx.getTenantId())) {
- return Boolean.FALSE;
- } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) {
- return Boolean.FALSE;
+ if (ctx.isTenantAdmin() || ctx.isCustomerUser() || ctx.isSystemAdmin()) {
+ switch (entityId.getEntityType()) {
+ case DEVICE:
+ if (ctx.isSystemAdmin()) {
+ callback.onSuccess(this, Boolean.FALSE);
} else {
- return Boolean.TRUE;
+ ListenableFuture deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
+ Futures.addCallback(deviceFuture, getCallback(callback, device -> {
+ if (device == null) {
+ return Boolean.FALSE;
+ } else {
+ if (!device.getTenantId().equals(ctx.getTenantId())) {
+ return Boolean.FALSE;
+ } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) {
+ return Boolean.FALSE;
+ } else {
+ return Boolean.TRUE;
+ }
+ }
+ }));
}
- }
- }));
+ return;
+ case ASSET:
+ if (ctx.isSystemAdmin()) {
+ callback.onSuccess(this, Boolean.FALSE);
+ } else {
+ ListenableFuture assetFuture = pluginCtx.assetService.findAssetByIdAsync(new AssetId(entityId.getId()));
+ Futures.addCallback(assetFuture, getCallback(callback, asset -> {
+ if (asset == null) {
+ return Boolean.FALSE;
+ } else {
+ if (!asset.getTenantId().equals(ctx.getTenantId())) {
+ return Boolean.FALSE;
+ } else if (ctx.isCustomerUser() && !asset.getCustomerId().equals(ctx.getCustomerId())) {
+ return Boolean.FALSE;
+ } else {
+ return Boolean.TRUE;
+ }
+ }
+ }));
+ }
+ return;
+ case RULE:
+ if (ctx.isCustomerUser()) {
+ callback.onSuccess(this, Boolean.FALSE);
+ } else {
+ ListenableFuture ruleFuture = pluginCtx.ruleService.findRuleByIdAsync(new RuleId(entityId.getId()));
+ Futures.addCallback(ruleFuture, getCallback(callback, rule -> rule != null && rule.getTenantId().equals(ctx.getTenantId())));
+ }
+ return;
+ case PLUGIN:
+ if (ctx.isCustomerUser()) {
+ callback.onSuccess(this, Boolean.FALSE);
+ } else {
+ ListenableFuture pluginFuture = pluginCtx.pluginService.findPluginByIdAsync(new PluginId(entityId.getId()));
+ Futures.addCallback(pluginFuture, getCallback(callback, plugin -> plugin != null && plugin.getTenantId().equals(ctx.getTenantId())));
+ }
+ return;
+ case CUSTOMER:
+ if (ctx.isSystemAdmin()) {
+ callback.onSuccess(this, Boolean.FALSE);
+ } else {
+ ListenableFuture customerFuture = pluginCtx.customerService.findCustomerByIdAsync(new CustomerId(entityId.getId()));
+ Futures.addCallback(customerFuture, getCallback(callback, customer -> {
+ if (customer == null) {
+ return Boolean.FALSE;
+ } else {
+ if (!customer.getTenantId().equals(ctx.getTenantId())) {
+ return Boolean.FALSE;
+ } else if (ctx.isCustomerUser() && !customer.getId().equals(ctx.getCustomerId())) {
+ return Boolean.FALSE;
+ } else {
+ return Boolean.TRUE;
+ }
+ }
+ }));
+ }
+ return;
+ case TENANT:
+ if (ctx.isCustomerUser()) {
+ callback.onSuccess(this, Boolean.FALSE);
+ } else if (ctx.isSystemAdmin()) {
+ callback.onSuccess(this, Boolean.TRUE);
+ } else {
+ ListenableFuture tenantFuture = pluginCtx.tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
+ Futures.addCallback(tenantFuture, getCallback(callback, tenant -> tenant != null && tenant.getId().equals(ctx.getTenantId())));
+ }
+ return;
+ default:
+ //TODO: add support of other entities
+ throw new IllegalStateException("Not Implemented!");
+ }
} else {
callback.onSuccess(this, Boolean.FALSE);
}
@@ -297,8 +380,8 @@ public final class PluginProcessingContext implements PluginContext {
}
@Override
- public Optional resolve(DeviceId deviceId) {
- return pluginCtx.routingService.resolve(deviceId);
+ public Optional resolve(EntityId entityId) {
+ return pluginCtx.routingService.resolveById(entityId);
}
@Override
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java
index b09b72e74b..43ddce6105 100644
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java
@@ -25,8 +25,13 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
import org.thingsboard.server.common.data.id.PluginId;
+import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
+import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.plugin.PluginService;
+import org.thingsboard.server.dao.rule.RuleService;
+import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
@@ -46,7 +51,12 @@ public final class SharedPluginProcessingContext {
final ActorRef currentActor;
final ActorSystemContext systemContext;
final PluginWebSocketMsgEndpoint msgEndpoint;
+ final AssetService assetService;
final DeviceService deviceService;
+ final RuleService ruleService;
+ final PluginService pluginService;
+ final CustomerService customerService;
+ final TenantService tenantService;
final TimeseriesService tsService;
final AttributesService attributesService;
final ClusterRpcService rpcService;
@@ -65,9 +75,14 @@ public final class SharedPluginProcessingContext {
this.msgEndpoint = sysContext.getWsMsgEndpoint();
this.tsService = sysContext.getTsService();
this.attributesService = sysContext.getAttributesService();
+ this.assetService = sysContext.getAssetService();
this.deviceService = sysContext.getDeviceService();
this.rpcService = sysContext.getRpcService();
this.routingService = sysContext.getRoutingService();
+ this.ruleService = sysContext.getRuleService();
+ this.pluginService = sysContext.getPluginService();
+ this.customerService = sysContext.getCustomerService();
+ this.tenantService = sysContext.getTenantService();
}
public PluginId getPluginId() {
@@ -89,7 +104,7 @@ public final class SharedPluginProcessingContext {
}
private void forward(DeviceId deviceId, T msg, BiConsumer rpcFunction) {
- Optional instance = routingService.resolve(deviceId);
+ Optional instance = routingService.resolveById(deviceId);
if (instance.isPresent()) {
log.trace("[{}] Forwarding msg {} to remote device actor!", pluginId, msg);
rpcFunction.accept(instance.get(), msg);
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationCallback.java b/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationCallback.java
index 707afa50dd..c8d79b41b2 100644
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationCallback.java
+++ b/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationCallback.java
@@ -38,7 +38,7 @@ public class ValidationCallback implements PluginCallback {
if (value) {
action.accept(ctx);
} else {
- onFailure(ctx, new UnauthorizedException());
+ onFailure(ctx, new UnauthorizedException("Permission denied."));
}
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
index 54160bc0a8..65d2c69321 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
@@ -230,7 +230,7 @@ public class DefaultActorService implements ActorService {
@Override
public void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId) {
DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId);
- Optional address = actorContext.getRoutingService().resolve(deviceId);
+ Optional address = actorContext.getRoutingService().resolveById(deviceId);
if (address.isPresent()) {
rpcService.tell(address.get(), msg);
} else {
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
index f806687056..b451d773f5 100644
--- a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java
@@ -116,7 +116,7 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
@Override
public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {
if (pendingMap.size() > 0 || subscribedToAttributeUpdates || subscribedToRpcCommands) {
- Optional newTargetServer = systemContext.getRoutingService().resolve(getDeviceId());
+ Optional newTargetServer = systemContext.getRoutingService().resolveById(getDeviceId());
if (!newTargetServer.equals(currentTargetServer)) {
firstMsg = true;
currentTargetServer = newTargetServer;
diff --git a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
index b8d13ec4c5..483c034d72 100644
--- a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java
@@ -81,13 +81,13 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
}
protected Optional forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward) {
- Optional address = systemContext.getRoutingService().resolve(toForward.getDeviceId());
+ Optional address = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
forwardToAppActor(ctx, toForward, address);
return address;
}
protected Optional forwardToAppActorIfAdressChanged(ActorContext ctx, ToDeviceActorMsg toForward, Optional oldAddress) {
- Optional newAddress = systemContext.getRoutingService().resolve(toForward.getDeviceId());
+ Optional newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
if (!newAddress.equals(oldAddress)) {
if (newAddress.isPresent()) {
systemContext.getRpcService().tell(newAddress.get(),
diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
new file mode 100644
index 0000000000..24497d461f
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
@@ -0,0 +1,234 @@
+/**
+ * Copyright © 2016-2017 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.controller;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import org.thingsboard.server.common.data.Customer;
+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.TenantId;
+import org.thingsboard.server.common.data.page.TextPageData;
+import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.dao.asset.AssetSearchQuery;
+import org.thingsboard.server.dao.exception.IncorrectParameterException;
+import org.thingsboard.server.dao.model.ModelConstants;
+import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.service.security.model.SecurityUser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RestController
+@RequestMapping("/api")
+public class AssetController extends BaseController {
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/asset/{assetId}", method = RequestMethod.GET)
+ @ResponseBody
+ public Asset getAssetById(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
+ checkParameter("assetId", strAssetId);
+ try {
+ AssetId assetId = new AssetId(toUUID(strAssetId));
+ return checkAssetId(assetId);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/asset", method = RequestMethod.POST)
+ @ResponseBody
+ public Asset saveAsset(@RequestBody Asset asset) throws ThingsboardException {
+ try {
+ asset.setTenantId(getCurrentUser().getTenantId());
+ return checkNotNull(assetService.saveAsset(asset));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/asset/{assetId}", method = RequestMethod.DELETE)
+ @ResponseStatus(value = HttpStatus.OK)
+ public void deleteAsset(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
+ checkParameter("assetId", strAssetId);
+ try {
+ AssetId assetId = new AssetId(toUUID(strAssetId));
+ checkAssetId(assetId);
+ assetService.deleteAsset(assetId);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/customer/{customerId}/asset/{assetId}", method = RequestMethod.POST)
+ @ResponseBody
+ public Asset assignAssetToCustomer(@PathVariable("customerId") String strCustomerId,
+ @PathVariable("assetId") String strAssetId) throws ThingsboardException {
+ checkParameter("customerId", strCustomerId);
+ checkParameter("assetId", strAssetId);
+ try {
+ CustomerId customerId = new CustomerId(toUUID(strCustomerId));
+ checkCustomerId(customerId);
+
+ AssetId assetId = new AssetId(toUUID(strAssetId));
+ checkAssetId(assetId);
+
+ return checkNotNull(assetService.assignAssetToCustomer(assetId, customerId));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/customer/asset/{assetId}", method = RequestMethod.DELETE)
+ @ResponseBody
+ public Asset unassignAssetFromCustomer(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
+ checkParameter("assetId", strAssetId);
+ try {
+ AssetId assetId = new AssetId(toUUID(strAssetId));
+ Asset asset = checkAssetId(assetId);
+ if (asset.getCustomerId() == null || asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
+ throw new IncorrectParameterException("Asset isn't assigned to any customer!");
+ }
+ return checkNotNull(assetService.unassignAssetFromCustomer(assetId));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/customer/public/asset/{assetId}", method = RequestMethod.POST)
+ @ResponseBody
+ public Asset assignAssetToPublicCustomer(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
+ checkParameter("assetId", strAssetId);
+ try {
+ AssetId assetId = new AssetId(toUUID(strAssetId));
+ Asset asset = checkAssetId(assetId);
+ Customer publicCustomer = customerService.findOrCreatePublicCustomer(asset.getTenantId());
+ return checkNotNull(assetService.assignAssetToCustomer(assetId, publicCustomer.getId()));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/tenant/assets", params = {"limit"}, method = RequestMethod.GET)
+ @ResponseBody
+ public TextPageData getTenantAssets(
+ @RequestParam int limit,
+ @RequestParam(required = false) String textSearch,
+ @RequestParam(required = false) String idOffset,
+ @RequestParam(required = false) String textOffset) throws ThingsboardException {
+ try {
+ TenantId tenantId = getCurrentUser().getTenantId();
+ TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
+ return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+ @RequestMapping(value = "/tenant/assets", params = {"assetName"}, method = RequestMethod.GET)
+ @ResponseBody
+ public Asset getTenantAsset(
+ @RequestParam String assetName) throws ThingsboardException {
+ try {
+ TenantId tenantId = getCurrentUser().getTenantId();
+ return checkNotNull(assetService.findAssetByTenantIdAndName(tenantId, assetName));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/customer/{customerId}/assets", params = {"limit"}, method = RequestMethod.GET)
+ @ResponseBody
+ public TextPageData getCustomerAssets(
+ @PathVariable("customerId") String strCustomerId,
+ @RequestParam int limit,
+ @RequestParam(required = false) String textSearch,
+ @RequestParam(required = false) String idOffset,
+ @RequestParam(required = false) String textOffset) throws ThingsboardException {
+ checkParameter("customerId", strCustomerId);
+ try {
+ TenantId tenantId = getCurrentUser().getTenantId();
+ CustomerId customerId = new CustomerId(toUUID(strCustomerId));
+ checkCustomerId(customerId);
+ TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
+ return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/assets", params = {"assetIds"}, method = RequestMethod.GET)
+ @ResponseBody
+ public List getAssetsByIds(
+ @RequestParam("assetIds") String[] strAssetIds) throws ThingsboardException {
+ checkArrayParameter("assetIds", strAssetIds);
+ try {
+ SecurityUser user = getCurrentUser();
+ TenantId tenantId = user.getTenantId();
+ CustomerId customerId = user.getCustomerId();
+ List assetIds = new ArrayList<>();
+ for (String strAssetId : strAssetIds) {
+ assetIds.add(new AssetId(toUUID(strAssetId)));
+ }
+ ListenableFuture> assets;
+ if (customerId == null || customerId.isNullUid()) {
+ assets = assetService.findAssetsByTenantIdAndIdsAsync(tenantId, assetIds);
+ } else {
+ assets = assetService.findAssetsByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, assetIds);
+ }
+ return checkNotNull(assets.get());
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/assets", method = RequestMethod.POST)
+ @ResponseBody
+ public List findByQuery(@RequestBody AssetSearchQuery query) throws ThingsboardException {
+ checkNotNull(query);
+ checkNotNull(query.getParameters());
+ checkNotNull(query.getAssetTypes());
+ checkEntityId(query.getParameters().getEntityId());
+ try {
+ List assets = checkNotNull(assetService.findAssetsByQuery(query).get());
+ assets = assets.stream().filter(asset -> {
+ try {
+ checkAsset(asset);
+ return true;
+ } catch (ThingsboardException e) {
+ return false;
+ }
+ }).collect(Collectors.toList());
+ return assets;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index 1b6696fd68..d4adebeccc 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -24,10 +24,8 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.thingsboard.server.actors.service.ActorService;
-import org.thingsboard.server.common.data.Customer;
-import org.thingsboard.server.common.data.Dashboard;
-import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.User;
+import org.thingsboard.server.common.data.*;
+import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
@@ -38,6 +36,7 @@ import org.thingsboard.server.common.data.rule.RuleMetaData;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
+import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
@@ -46,6 +45,7 @@ import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.plugin.PluginService;
+import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
@@ -80,6 +80,9 @@ public abstract class BaseController {
@Autowired
protected DeviceService deviceService;
+ @Autowired
+ protected AssetService assetService;
+
@Autowired
protected DeviceCredentialsService deviceCredentialsService;
@@ -104,6 +107,9 @@ public abstract class BaseController {
@Autowired
protected ActorService actorService;
+ @Autowired
+ protected RelationService relationService;
+
@ExceptionHandler(ThingsboardException.class)
public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
@@ -253,6 +259,43 @@ public abstract class BaseController {
}
}
+ protected void checkEntityId(EntityId entityId) throws ThingsboardException {
+ try {
+ checkNotNull(entityId);
+ validateId(entityId.getId(), "Incorrect entityId " + entityId);
+ switch (entityId.getEntityType()) {
+ case DEVICE:
+ checkDevice(deviceService.findDeviceById(new DeviceId(entityId.getId())));
+ return;
+ case CUSTOMER:
+ checkCustomerId(new CustomerId(entityId.getId()));
+ return;
+ case TENANT:
+ checkTenantId(new TenantId(entityId.getId()));
+ return;
+ case PLUGIN:
+ checkPlugin(new PluginId(entityId.getId()));
+ return;
+ case RULE:
+ checkRule(new RuleId(entityId.getId()));
+ return;
+ case ASSET:
+ checkAsset(assetService.findAssetById(new AssetId(entityId.getId())));
+ return;
+ case DASHBOARD:
+ checkDashboardId(new DashboardId(entityId.getId()));
+ return;
+ case USER:
+ checkUserId(new UserId(entityId.getId()));
+ return;
+ default:
+ throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
+ }
+ } catch (Exception e) {
+ throw handleException(e, false);
+ }
+ }
+
Device checkDeviceId(DeviceId deviceId) throws ThingsboardException {
try {
validateId(deviceId, "Incorrect deviceId " + deviceId);
@@ -264,7 +307,7 @@ public abstract class BaseController {
}
}
- private void checkDevice(Device device) throws ThingsboardException {
+ protected void checkDevice(Device device) throws ThingsboardException {
checkNotNull(device);
checkTenantId(device.getTenantId());
if (device.getCustomerId() != null && !device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
@@ -272,6 +315,25 @@ public abstract class BaseController {
}
}
+ Asset checkAssetId(AssetId assetId) throws ThingsboardException {
+ try {
+ validateId(assetId, "Incorrect assetId " + assetId);
+ Asset asset = assetService.findAssetById(assetId);
+ checkAsset(asset);
+ return asset;
+ } catch (Exception e) {
+ throw handleException(e, false);
+ }
+ }
+
+ protected void checkAsset(Asset asset) throws ThingsboardException {
+ checkNotNull(asset);
+ checkTenantId(asset.getTenantId());
+ if (asset.getCustomerId() != null && !asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
+ checkCustomerId(asset.getCustomerId());
+ }
+ }
+
WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException {
try {
validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId);
@@ -318,14 +380,26 @@ public abstract class BaseController {
try {
validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
Dashboard dashboard = dashboardService.findDashboardById(dashboardId);
- checkDashboard(dashboard);
+ checkDashboard(dashboard, true);
return dashboard;
} catch (Exception e) {
throw handleException(e, false);
}
}
- private void checkDashboard(Dashboard dashboard) throws ThingsboardException {
+ DashboardInfo checkDashboardInfoId(DashboardId dashboardId) throws ThingsboardException {
+ try {
+ validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
+ DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId);
+ SecurityUser authUser = getCurrentUser();
+ checkDashboard(dashboardInfo, authUser.getAuthority() != Authority.SYS_ADMIN);
+ return dashboardInfo;
+ } catch (Exception e) {
+ throw handleException(e, false);
+ }
+ }
+
+ private void checkDashboard(DashboardInfo dashboard, boolean checkCustomerId) throws ThingsboardException {
checkNotNull(dashboard);
checkTenantId(dashboard.getTenantId());
SecurityUser authUser = getCurrentUser();
@@ -335,7 +409,8 @@ public abstract class BaseController {
ThingsboardErrorCode.PERMISSION_DENIED);
}
}
- if (dashboard.getCustomerId() != null && !dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
+ if (checkCustomerId &&
+ dashboard.getCustomerId() != null && !dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
checkCustomerId(dashboard.getCustomerId());
}
}
@@ -387,6 +462,16 @@ public abstract class BaseController {
return plugin;
}
+ protected PluginMetaData checkPlugin(PluginId pluginId) throws ThingsboardException {
+ checkNotNull(pluginId);
+ return checkPlugin(pluginService.findPluginById(pluginId));
+ }
+
+ protected RuleMetaData checkRule(RuleId ruleId) throws ThingsboardException {
+ checkNotNull(ruleId);
+ return checkRule(ruleService.findRuleById(ruleId));
+ }
+
protected RuleMetaData checkRule(RuleMetaData rule) throws ThingsboardException {
checkNotNull(rule);
SecurityUser authUser = getCurrentUser();
@@ -412,7 +497,8 @@ public abstract class BaseController {
if (request.getHeader("x-forwarded-port") != null) {
try {
serverPort = request.getIntHeader("x-forwarded-port");
- } catch (NumberFormatException e) {}
+ } catch (NumberFormatException e) {
+ }
}
String baseUrl = String.format("%s://%s:%d",
diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
index 3812610d20..2a6416ce98 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
@@ -41,6 +41,19 @@ public class DashboardController extends BaseController {
return System.currentTimeMillis();
}
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET)
+ @ResponseBody
+ public DashboardInfo getDashboardInfoById(@PathVariable("dashboardId") String strDashboardId) throws ThingsboardException {
+ checkParameter("dashboardId", strDashboardId);
+ try {
+ DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
+ return checkDashboardInfoId(dashboardId);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/{dashboardId}", method = RequestMethod.GET)
@ResponseBody
@@ -132,6 +145,25 @@ public class DashboardController extends BaseController {
}
}
+ @PreAuthorize("hasAuthority('SYS_ADMIN')")
+ @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = { "limit" }, method = RequestMethod.GET)
+ @ResponseBody
+ public TextPageData getTenantDashboards(
+ @PathVariable("tenantId") String strTenantId,
+ @RequestParam int limit,
+ @RequestParam(required = false) String textSearch,
+ @RequestParam(required = false) String idOffset,
+ @RequestParam(required = false) String textOffset) throws ThingsboardException {
+ try {
+ TenantId tenantId = new TenantId(toUUID(strTenantId));
+ checkTenantId(tenantId);
+ TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
+ return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink));
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/dashboards", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
index bebab8b880..7cd381c57a 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -24,19 +24,18 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
+import org.thingsboard.server.dao.device.DeviceSearchQuery;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.exception.ThingsboardException;
-import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.ArrayList;
import java.util.List;
-import java.util.UUID;
+import java.util.stream.Collectors;
@RestController
@RequestMapping("/api")
@@ -238,4 +237,28 @@ public class DeviceController extends BaseController {
throw handleException(e);
}
}
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/devices", method = RequestMethod.POST)
+ @ResponseBody
+ public List findByQuery(@RequestBody DeviceSearchQuery query) throws ThingsboardException {
+ checkNotNull(query);
+ checkNotNull(query.getParameters());
+ checkNotNull(query.getDeviceTypes());
+ checkEntityId(query.getParameters().getEntityId());
+ try {
+ List devices = checkNotNull(deviceService.findDevicesByQuery(query).get());
+ devices = devices.stream().filter(device -> {
+ try {
+ checkDevice(device);
+ return true;
+ } catch (ThingsboardException e) {
+ return false;
+ }
+ }).collect(Collectors.toList());
+ return devices;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
new file mode 100644
index 0000000000..0c1fd8bf73
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
@@ -0,0 +1,194 @@
+/**
+ * Copyright © 2016-2017 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.controller;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.EntityIdFactory;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.dao.relation.EntityRelationsQuery;
+import org.thingsboard.server.exception.ThingsboardErrorCode;
+import org.thingsboard.server.exception.ThingsboardException;
+
+import java.util.List;
+
+
+@RestController
+@RequestMapping("/api")
+public class EntityRelationController extends BaseController {
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/relation", method = RequestMethod.POST)
+ @ResponseStatus(value = HttpStatus.OK)
+ public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException {
+ try {
+ checkNotNull(relation);
+ checkEntityId(relation.getFrom());
+ checkEntityId(relation.getTo());
+ relationService.saveRelation(relation).get();
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {"fromId", "fromType", "relationType", "toId", "toType"})
+ @ResponseStatus(value = HttpStatus.OK)
+ public void deleteRelation(@RequestParam("fromId") String strFromId,
+ @RequestParam("fromType") String strFromType, @RequestParam("relationType") String strRelationType,
+ @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException {
+ checkParameter("fromId", strFromId);
+ checkParameter("fromType", strFromType);
+ checkParameter("relationType", strRelationType);
+ checkParameter("toId", strToId);
+ checkParameter("toType", strToType);
+ EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
+ EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
+ checkEntityId(fromId);
+ checkEntityId(toId);
+ try {
+ Boolean found = relationService.deleteRelation(fromId, toId, strRelationType).get();
+ if (!found) {
+ throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
+ }
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"id", "type"})
+ @ResponseStatus(value = HttpStatus.OK)
+ public void deleteRelations(@RequestParam("entityId") String strId,
+ @RequestParam("entityType") String strType) throws ThingsboardException {
+ checkParameter("entityId", strId);
+ checkParameter("entityType", strType);
+ EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId);
+ checkEntityId(entityId);
+ try {
+ relationService.deleteEntityRelations(entityId).get();
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType", "toId", "toType"})
+ @ResponseStatus(value = HttpStatus.OK)
+ public void checkRelation(@RequestParam("fromId") String strFromId,
+ @RequestParam("fromType") String strFromType, @RequestParam("relationType") String strRelationType,
+ @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException {
+ try {
+ checkParameter("fromId", strFromId);
+ checkParameter("fromType", strFromType);
+ checkParameter("relationType", strRelationType);
+ checkParameter("toId", strToId);
+ checkParameter("toType", strToType);
+ EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
+ EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
+ checkEntityId(fromId);
+ checkEntityId(toId);
+ Boolean found = relationService.checkRelation(fromId, toId, strRelationType).get();
+ if (!found) {
+ throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
+ }
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType"})
+ @ResponseBody
+ public List findByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType) throws ThingsboardException {
+ checkParameter("fromId", strFromId);
+ checkParameter("fromType", strFromType);
+ EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
+ checkEntityId(entityId);
+ try {
+ return checkNotNull(relationService.findByFrom(entityId).get());
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"})
+ @ResponseBody
+ public List findByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType
+ , @RequestParam("relationType") String strRelationType) throws ThingsboardException {
+ checkParameter("fromId", strFromId);
+ checkParameter("fromType", strFromType);
+ checkParameter("relationType", strRelationType);
+ EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
+ checkEntityId(entityId);
+ try {
+ return checkNotNull(relationService.findByFromAndType(entityId, strRelationType).get());
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType"})
+ @ResponseBody
+ public List findByTo(@RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException {
+ checkParameter("toId", strToId);
+ checkParameter("toType", strToType);
+ EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
+ checkEntityId(entityId);
+ try {
+ return checkNotNull(relationService.findByTo(entityId).get());
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType", "relationType"})
+ @ResponseBody
+ public List findByTo(@RequestParam("toId") String strToId, @RequestParam("toType") String strToType
+ , @RequestParam("relationType") String strRelationType) throws ThingsboardException {
+ checkParameter("toId", strToId);
+ checkParameter("toType", strToType);
+ checkParameter("relationType", strRelationType);
+ EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
+ checkEntityId(entityId);
+ try {
+ return checkNotNull(relationService.findByToAndType(entityId, strRelationType).get());
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+ @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+ @RequestMapping(value = "/relations", method = RequestMethod.POST)
+ @ResponseBody
+ public List findByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException {
+ checkNotNull(query);
+ checkNotNull(query.getParameters());
+ checkNotNull(query.getFilters());
+ checkEntityId(query.getParameters().getEntityId());
+ try {
+ return checkNotNull(relationService.findByQuery(query).get());
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java
index 1bf6aa984c..dc35324bdd 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EventController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java
@@ -18,7 +18,6 @@ package org.thingsboard.server.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
-import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TimePageData;
@@ -59,7 +58,7 @@ public class EventController extends BaseController {
ThingsboardErrorCode.PERMISSION_DENIED);
}
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
- return checkNotNull(eventService.findEvents(tenantId, getEntityId(strEntityType, strEntityId), eventType, pageLink));
+ return checkNotNull(eventService.findEvents(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), eventType, pageLink));
} catch (Exception e) {
throw handleException(e);
}
@@ -88,29 +87,10 @@ public class EventController extends BaseController {
ThingsboardErrorCode.PERMISSION_DENIED);
}
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
- return checkNotNull(eventService.findEvents(tenantId, getEntityId(strEntityType, strEntityId), pageLink));
+ return checkNotNull(eventService.findEvents(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
-
- private EntityId getEntityId(String strEntityType, String strEntityId) throws ThingsboardException {
- EntityId entityId;
- EntityType entityType = EntityType.valueOf(strEntityType);
- switch (entityType) {
- case RULE:
- entityId = new RuleId(toUUID(strEntityId));
- break;
- case PLUGIN:
- entityId = new PluginId(toUUID(strEntityId));
- break;
- case DEVICE:
- entityId = new DeviceId(toUUID(strEntityId));
- break;
- default:
- throw new ThingsboardException("EntityType ['" + entityType + "'] is incorrect!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
- }
- return entityId;
- }
}
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java
index 089ade8f5d..c0efbfc1d6 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java
@@ -15,11 +15,13 @@
*/
package org.thingsboard.server.service.cluster.routing;
+import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.service.cluster.discovery.ServerInstance;
import java.util.Optional;
+import java.util.UUID;
/**
* @author Andrew Shvayka
@@ -28,6 +30,8 @@ public interface ClusterRoutingService {
ServerAddress getCurrentServer();
- Optional resolve(UUIDBased entityId);
+ Optional resolveByUuid(UUID uuid);
+
+ Optional resolveById(EntityId entityId);
}
diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java
index b57e15eadf..c29833b910 100644
--- a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java
+++ b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
+import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
@@ -31,6 +32,7 @@ import org.thingsboard.server.utils.MiscUtils;
import javax.annotation.PostConstruct;
import java.util.Optional;
+import java.util.UUID;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
@@ -77,13 +79,18 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D
}
@Override
- public Optional resolve(UUIDBased entityId) {
- Assert.notNull(entityId);
+ public Optional resolveById(EntityId entityId) {
+ return resolveByUuid(entityId.getId());
+ }
+
+ @Override
+ public Optional resolveByUuid(UUID uuid) {
+ Assert.notNull(uuid);
if (circle.isEmpty()) {
return Optional.empty();
}
- Long hash = hashFunction.newHasher().putLong(entityId.getId().getMostSignificantBits())
- .putLong(entityId.getId().getLeastSignificantBits()).hash().asLong();
+ Long hash = hashFunction.newHasher().putLong(uuid.getMostSignificantBits())
+ .putLong(uuid.getLeastSignificantBits()).hash().asLong();
if (!circle.containsKey(hash)) {
ConcurrentNavigableMap tailMap =
circle.tailMap(hash);
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index e64e9cd95c..69b1cd78a2 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -60,8 +60,8 @@ plugins:
# JWT Token parameters
security.jwt:
- tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:900}" # Number of seconds (15 mins)
- refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:3600}" # Seconds (1 hour)
+ tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:9000000}" # Number of seconds (15 mins)
+ refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:36000000}" # Seconds (1 hour)
tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}"
tokenSigningKey: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}"
diff --git a/application/src/test/java/org/thingsboard/server/actors/DefaultActorServiceTest.java b/application/src/test/java/org/thingsboard/server/actors/DefaultActorServiceTest.java
index 2940a62a93..857e0835e3 100644
--- a/application/src/test/java/org/thingsboard/server/actors/DefaultActorServiceTest.java
+++ b/application/src/test/java/org/thingsboard/server/actors/DefaultActorServiceTest.java
@@ -145,7 +145,7 @@ public class DefaultActorServiceTest {
ReflectionTestUtils.setField(actorContext, "eventService", eventService);
- when(routingService.resolve(any())).thenReturn(Optional.empty());
+ when(routingService.resolveById((EntityId) any())).thenReturn(Optional.empty());
when(discoveryService.getCurrentServer()).thenReturn(serverInstance);
@@ -239,7 +239,7 @@ public class DefaultActorServiceTest {
List expected = new ArrayList<>();
expected.add(new BasicTsKvEntry(ts, entry1));
expected.add(new BasicTsKvEntry(ts, entry2));
- verify(tsService, Mockito.timeout(5000)).save(DataConstants.DEVICE, deviceId, expected);
+ verify(tsService, Mockito.timeout(5000)).save(deviceId, expected);
}
}
diff --git a/common/data/pom.xml b/common/data/pom.xml
index aca4c9c0d0..ffee17c00d 100644
--- a/common/data/pom.xml
+++ b/common/data/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 1.2.3-SNAPSHOT
+ 1.3.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java
index 6b312836b3..92e8655c72 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java
@@ -28,6 +28,7 @@ public class Device extends SearchTextBased {
private TenantId tenantId;
private CustomerId customerId;
private String name;
+ private String type;
private JsonNode additionalInfo;
public Device() {
@@ -43,6 +44,7 @@ public class Device extends SearchTextBased {
this.tenantId = device.getTenantId();
this.customerId = device.getCustomerId();
this.name = device.getName();
+ this.type = device.getType();
this.additionalInfo = device.getAdditionalInfo();
}
@@ -70,6 +72,14 @@ public class Device extends SearchTextBased {
this.name = name;
}
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
public JsonNode getAdditionalInfo() {
return additionalInfo;
}
@@ -90,6 +100,7 @@ public class Device extends SearchTextBased {
result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode());
result = prime * result + ((customerId == null) ? 0 : customerId.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
return result;
}
@@ -118,6 +129,11 @@ public class Device extends SearchTextBased {
return false;
} else if (!name.equals(other.name))
return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ } else if (!type.equals(other.type))
+ return false;
if (tenantId == null) {
if (other.tenantId != null)
return false;
@@ -135,6 +151,8 @@ public class Device extends SearchTextBased {
builder.append(customerId);
builder.append(", name=");
builder.append(name);
+ builder.append(", type=");
+ builder.append(type);
builder.append(", additionalInfo=");
builder.append(additionalInfo);
builder.append(", createdTime=");
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
index 8dac8f5e20..ebf42f22ac 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
@@ -19,5 +19,5 @@ package org.thingsboard.server.common.data;
* @author Andrew Shvayka
*/
public enum EntityType {
- TENANT, DEVICE, CUSTOMER, RULE, PLUGIN
+ TENANT, CUSTOMER, USER, RULE, PLUGIN, DASHBOARD, ASSET, DEVICE, ALARM
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
new file mode 100644
index 0000000000..19d671cab2
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.alarm;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Data;
+import org.thingsboard.server.common.data.BaseData;
+import org.thingsboard.server.common.data.id.EntityId;
+
+/**
+ * Created by ashvayka on 11.05.17.
+ */
+@Data
+public class Alarm extends BaseData {
+
+ private long startTs;
+ private long endTs;
+ private long ackTs;
+ private long clearTs;
+ private String type;
+ private EntityId originator;
+ private AlarmSeverity severity;
+ private AlarmStatus status;
+ private JsonNode details;
+ private boolean propagate;
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmId.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmId.java
new file mode 100644
index 0000000000..ad0fd5b98a
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmId.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.alarm;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.UUIDBased;
+
+import java.util.UUID;
+
+public class AlarmId extends UUIDBased implements EntityId {
+
+ private static final long serialVersionUID = 1L;
+
+ @JsonCreator
+ public AlarmId(@JsonProperty("id") UUID id) {
+ super(id);
+ }
+
+ public static AlarmId fromString(String alarmId) {
+ return new AlarmId(UUID.fromString(alarmId));
+ }
+
+ @JsonIgnore
+ @Override
+ public EntityType getEntityType() {
+ return EntityType.ALARM;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
new file mode 100644
index 0000000000..04d23a55c0
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.alarm;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.page.TimePageLink;
+
+/**
+ * Created by ashvayka on 11.05.17.
+ */
+@Data
+public class AlarmQuery {
+
+ private EntityId affectedEntityId;
+ private TimePageLink pageLink;
+ private AlarmStatus status;
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java
new file mode 100644
index 0000000000..ed73027ca6
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.alarm;
+
+/**
+ * Created by ashvayka on 11.05.17.
+ */
+public enum AlarmSeverity {
+
+ CRITICAL, MAJOR, MINOR, WARNING, INDETERMINATE;
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java
new file mode 100644
index 0000000000..54fbc9dc03
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.alarm;
+
+/**
+ * Created by ashvayka on 11.05.17.
+ */
+public enum AlarmStatus {
+
+ ACTIVE_UNACK, ACTIVE_ACK, CLEARED_UNACK, CLEARED_ACK;
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java
new file mode 100644
index 0000000000..2e20b7745c
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java
@@ -0,0 +1,166 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.asset;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.thingsboard.server.common.data.SearchTextBased;
+import org.thingsboard.server.common.data.id.AssetId;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.TenantId;
+
+public class Asset extends SearchTextBased {
+
+ private static final long serialVersionUID = 2807343040519543363L;
+
+ private TenantId tenantId;
+ private CustomerId customerId;
+ private String name;
+ private String type;
+ private JsonNode additionalInfo;
+
+ public Asset() {
+ super();
+ }
+
+ public Asset(AssetId id) {
+ super(id);
+ }
+
+ public Asset(Asset asset) {
+ super(asset);
+ this.tenantId = asset.getTenantId();
+ this.customerId = asset.getCustomerId();
+ this.name = asset.getName();
+ this.type = asset.getType();
+ this.additionalInfo = asset.getAdditionalInfo();
+ }
+
+ public TenantId getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(TenantId tenantId) {
+ this.tenantId = tenantId;
+ }
+
+ public CustomerId getCustomerId() {
+ return customerId;
+ }
+
+ public void setCustomerId(CustomerId customerId) {
+ this.customerId = customerId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public JsonNode getAdditionalInfo() {
+ return additionalInfo;
+ }
+
+ public void setAdditionalInfo(JsonNode additionalInfo) {
+ this.additionalInfo = additionalInfo;
+ }
+
+ @Override
+ public String getSearchText() {
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode());
+ result = prime * result + ((customerId == null) ? 0 : customerId.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Asset other = (Asset) obj;
+ if (additionalInfo == null) {
+ if (other.additionalInfo != null)
+ return false;
+ } else if (!additionalInfo.equals(other.additionalInfo))
+ return false;
+ if (customerId == null) {
+ if (other.customerId != null)
+ return false;
+ } else if (!customerId.equals(other.customerId))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ } else if (!type.equals(other.type))
+ return false;
+ if (tenantId == null) {
+ if (other.tenantId != null)
+ return false;
+ } else if (!tenantId.equals(other.tenantId))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Asset [tenantId=");
+ builder.append(tenantId);
+ builder.append(", customerId=");
+ builder.append(customerId);
+ builder.append(", name=");
+ builder.append(name);
+ builder.append(", type=");
+ builder.append(type);
+ builder.append(", additionalInfo=");
+ builder.append(additionalInfo);
+ builder.append(", createdTime=");
+ builder.append(createdTime);
+ builder.append(", id=");
+ builder.append(id);
+ builder.append("]");
+ return builder.toString();
+ }
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/AssetId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/AssetId.java
new file mode 100644
index 0000000000..049116e876
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/AssetId.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.id;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.thingsboard.server.common.data.EntityType;
+
+import java.util.UUID;
+
+public class AssetId extends UUIDBased implements EntityId {
+
+ private static final long serialVersionUID = 1L;
+
+ @JsonCreator
+ public AssetId(@JsonProperty("id") UUID id) {
+ super(id);
+ }
+
+ public static AssetId fromString(String assetId) {
+ return new AssetId(UUID.fromString(assetId));
+ }
+
+ @JsonIgnore
+ @Override
+ public EntityType getEntityType() {
+ return EntityType.ASSET;
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/DashboardId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/DashboardId.java
index 632d5c23fb..a9a3441528 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/id/DashboardId.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/DashboardId.java
@@ -18,12 +18,24 @@ package org.thingsboard.server.common.data.id;
import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
+import org.thingsboard.server.common.data.EntityType;
-public class DashboardId extends UUIDBased {
+public class DashboardId extends UUIDBased implements EntityId {
@JsonCreator
- public DashboardId(@JsonProperty("id") UUID id){
+ public DashboardId(@JsonProperty("id") UUID id) {
super(id);
}
+
+ public static DashboardId fromString(String dashboardId) {
+ return new DashboardId(UUID.fromString(dashboardId));
+ }
+
+ @JsonIgnore
+ @Override
+ public EntityType getEntityType() {
+ return EntityType.DASHBOARD;
+ }
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java
index f0caec5e39..17d69ac939 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java
@@ -16,6 +16,8 @@
package org.thingsboard.server.common.data.id;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.thingsboard.server.common.data.EntityType;
import java.util.UUID;
@@ -23,6 +25,9 @@ import java.util.UUID;
/**
* @author Andrew Shvayka
*/
+
+@JsonDeserialize(using = EntityIdDeserializer.class)
+@JsonSerialize(using = EntityIdSerializer.class)
public interface EntityId {
UUID NULL_UUID = UUID.fromString("13814000-1dd2-11b2-8080-808080808080");
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdDeserializer.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdDeserializer.java
new file mode 100644
index 0000000000..f9f34c413e
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdDeserializer.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.id;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.io.IOException;
+
+/**
+ * Created by ashvayka on 11.05.17.
+ */
+public class EntityIdDeserializer extends JsonDeserializer {
+
+ @Override
+ public EntityId deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException, JsonProcessingException {
+ ObjectCodec oc = jsonParser.getCodec();
+ ObjectNode node = oc.readTree(jsonParser);
+ if (node.has("entityType") && node.has("id")) {
+ return EntityIdFactory.getByTypeAndId(node.get("entityType").asText(), node.get("id").asText());
+ } else {
+ throw new IOException("Missing entityType or id!");
+ }
+ }
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
new file mode 100644
index 0000000000..21093ce3f5
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.id;
+
+import org.thingsboard.server.common.data.EntityType;
+
+import java.util.UUID;
+
+/**
+ * Created by ashvayka on 25.04.17.
+ */
+public class EntityIdFactory {
+
+ public static EntityId getByTypeAndId(String type, String uuid) {
+ return getByTypeAndUuid(EntityType.valueOf(type), UUID.fromString(uuid));
+ }
+
+ public static EntityId getByTypeAndUuid(String type, UUID uuid) {
+ return getByTypeAndUuid(EntityType.valueOf(type), uuid);
+ }
+
+ public static EntityId getByTypeAndUuid(EntityType type, UUID uuid) {
+ switch (type) {
+ case TENANT:
+ return new TenantId(uuid);
+ case CUSTOMER:
+ return new CustomerId(uuid);
+ case USER:
+ return new UserId(uuid);
+ case RULE:
+ return new RuleId(uuid);
+ case PLUGIN:
+ return new PluginId(uuid);
+ case DASHBOARD:
+ return new DashboardId(uuid);
+ case DEVICE:
+ return new DeviceId(uuid);
+ case ASSET:
+ return new AssetId(uuid);
+ }
+ throw new IllegalArgumentException("EntityType " + type + " is not supported!");
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdSerializer.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdSerializer.java
new file mode 100644
index 0000000000..18ffaeaa5b
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdSerializer.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.id;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import java.io.IOException;
+
+/**
+ * Created by ashvayka on 11.05.17.
+ */
+public class EntityIdSerializer extends JsonSerializer {
+
+ @Override
+ public void serialize(EntityId value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
+ gen.writeStartObject();
+ gen.writeStringField("entityType", value.getEntityType().name());
+ gen.writeStringField("id", value.getId().toString());
+ gen.writeEndObject();
+ }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java
index 51b114012b..31b6642ef7 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java
@@ -18,12 +18,25 @@ package org.thingsboard.server.common.data.id;
import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
+import org.thingsboard.server.common.data.EntityType;
-public class UserId extends UUIDBased {
+public class UserId extends UUIDBased implements EntityId {
@JsonCreator
- public UserId(@JsonProperty("id") UUID id){
- super(id);
- }
+ public UserId(@JsonProperty("id") UUID id) {
+ super(id);
+ }
+
+ public static UserId fromString(String userId) {
+ return new UserId(UUID.fromString(userId));
+ }
+
+ @JsonIgnore
+ @Override
+ public EntityType getEntityType() {
+ return EntityType.USER;
+ }
+
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java
new file mode 100644
index 0000000000..8fcf2691af
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.relation;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.thingsboard.server.common.data.id.EntityId;
+
+import java.util.Objects;
+
+public class EntityRelation {
+
+ private static final long serialVersionUID = 2807343040519543363L;
+
+ public static final String CONTAINS_TYPE = "Contains";
+ public static final String MANAGES_TYPE = "Manages";
+
+ private EntityId from;
+ private EntityId to;
+ private String type;
+ private JsonNode additionalInfo;
+
+ public EntityRelation() {
+ super();
+ }
+
+ public EntityRelation(EntityId from, EntityId to, String type) {
+ this(from, to, type, null);
+ }
+
+ public EntityRelation(EntityId from, EntityId to, String type, JsonNode additionalInfo) {
+ this.from = from;
+ this.to = to;
+ this.type = type;
+ this.additionalInfo = additionalInfo;
+ }
+
+ public EntityRelation(EntityRelation device) {
+ this.from = device.getFrom();
+ this.to = device.getTo();
+ this.type = device.getType();
+ this.additionalInfo = device.getAdditionalInfo();
+ }
+
+ public EntityId getFrom() {
+ return from;
+ }
+
+ public void setFrom(EntityId from) {
+ this.from = from;
+ }
+
+ public EntityId getTo() {
+ return to;
+ }
+
+ public void setTo(EntityId to) {
+ this.to = to;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public JsonNode getAdditionalInfo() {
+ return additionalInfo;
+ }
+
+ public void setAdditionalInfo(JsonNode additionalInfo) {
+ this.additionalInfo = additionalInfo;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ EntityRelation relation = (EntityRelation) o;
+ return Objects.equals(from, relation.from) &&
+ Objects.equals(to, relation.to) &&
+ Objects.equals(type, relation.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(from, to, type);
+ }
+}
diff --git a/common/message/pom.xml b/common/message/pom.xml
index 01d3b45b32..5362f685ef 100644
--- a/common/message/pom.xml
+++ b/common/message/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 1.2.3-SNAPSHOT
+ 1.3.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/pom.xml b/common/pom.xml
index 9c7b51e1c8..798bec4ca7 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 1.2.3-SNAPSHOT
+ 1.3.0-SNAPSHOT
thingsboard
org.thingsboard
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index 4a2bb52a2f..c5377563a1 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 1.2.3-SNAPSHOT
+ 1.3.0-SNAPSHOT
common
org.thingsboard.common
diff --git a/dao/pom.xml b/dao/pom.xml
index 146b8c197e..cc3804feda 100644
--- a/dao/pom.xml
+++ b/dao/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 1.2.3-SNAPSHOT
+ 1.3.0-SNAPSHOT
thingsboard
org.thingsboard
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
new file mode 100644
index 0000000000..a116598a23
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright © 2016-2017 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.dao.alarm;
+
+/**
+ * Created by ashvayka on 11.05.17.
+ */
+public interface AlarmDao {
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
new file mode 100644
index 0000000000..35bd247602
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright © 2016-2017 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.dao.alarm;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import org.thingsboard.server.common.data.alarm.Alarm;
+import org.thingsboard.server.common.data.alarm.AlarmId;
+import org.thingsboard.server.common.data.alarm.AlarmQuery;
+import org.thingsboard.server.common.data.page.TimePageData;
+
+import java.util.Optional;
+
+/**
+ * Created by ashvayka on 11.05.17.
+ */
+public interface AlarmService {
+
+ Optional saveIfNotExists(Alarm alarm);
+
+ ListenableFuture updateAlarm(Alarm alarm);
+
+ ListenableFuture ackAlarm(Alarm alarm);
+
+ ListenableFuture clearAlarm(AlarmId alarmId);
+
+ ListenableFuture> findAlarms(AlarmQuery query);
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
new file mode 100644
index 0000000000..81f95b4821
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright © 2016-2017 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.dao.asset;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.asset.Asset;
+import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.dao.Dao;
+import org.thingsboard.server.dao.model.AssetEntity;
+import org.thingsboard.server.dao.model.DeviceEntity;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * The Interface AssetDao.
+ *
+ */
+public interface AssetDao extends Dao {
+
+ /**
+ * Save or update asset object
+ *
+ * @param asset the asset object
+ * @return saved asset object
+ */
+ AssetEntity save(Asset asset);
+
+ /**
+ * Find assets by tenantId and page link.
+ *
+ * @param tenantId the tenantId
+ * @param pageLink the page link
+ * @return the list of asset objects
+ */
+ List findAssetsByTenantId(UUID tenantId, TextPageLink pageLink);
+
+ /**
+ * Find assets by tenantId and assets Ids.
+ *
+ * @param tenantId the tenantId
+ * @param assetIds the asset Ids
+ * @return the list of asset objects
+ */
+ ListenableFuture> findAssetsByTenantIdAndIdsAsync(UUID tenantId, List assetIds);
+
+ /**
+ * Find assets by tenantId, customerId and page link.
+ *
+ * @param tenantId the tenantId
+ * @param customerId the customerId
+ * @param pageLink the page link
+ * @return the list of asset objects
+ */
+ List findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
+
+ /**
+ * Find assets by tenantId, customerId and assets Ids.
+ *
+ * @param tenantId the tenantId
+ * @param customerId the customerId
+ * @param assetIds the asset Ids
+ * @return the list of asset objects
+ */
+ ListenableFuture> findAssetsByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List assetIds);
+
+ /**
+ * Find assets by tenantId and asset name.
+ *
+ * @param tenantId the tenantId
+ * @param name the asset name
+ * @return the optional asset object
+ */
+ Optional findAssetsByTenantIdAndName(UUID tenantId, String name);
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java
new file mode 100644
index 0000000000..ed08c6ddf3
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright © 2016-2017 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.dao.asset;
+
+import com.datastax.driver.core.querybuilder.Select;
+import com.google.common.util.concurrent.ListenableFuture;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.asset.Asset;
+import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.dao.AbstractSearchTextDao;
+import org.thingsboard.server.dao.model.AssetEntity;
+
+import java.util.*;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
+import static org.thingsboard.server.dao.model.ModelConstants.*;
+
+@Component
+@Slf4j
+public class AssetDaoImpl extends AbstractSearchTextDao implements AssetDao {
+
+ @Override
+ protected Class getColumnFamilyClass() {
+ return AssetEntity.class;
+ }
+
+ @Override
+ protected String getColumnFamilyName() {
+ return ASSET_COLUMN_FAMILY_NAME;
+ }
+
+ @Override
+ public AssetEntity save(Asset asset) {
+ log.debug("Save asset [{}] ", asset);
+ return save(new AssetEntity(asset));
+ }
+
+ @Override
+ public List findAssetsByTenantId(UUID tenantId, TextPageLink pageLink) {
+ log.debug("Try to find assets by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
+ List assetEntities = findPageWithTextSearch(ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
+ Collections.singletonList(eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink);
+
+ log.trace("Found assets [{}] by tenantId [{}] and pageLink [{}]", assetEntities, tenantId, pageLink);
+ return assetEntities;
+ }
+
+ @Override
+ public ListenableFuture> findAssetsByTenantIdAndIdsAsync(UUID tenantId, List assetIds) {
+ log.debug("Try to find assets by tenantId [{}] and asset Ids [{}]", tenantId, assetIds);
+ Select select = select().from(getColumnFamilyName());
+ Select.Where query = select.where();
+ query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
+ query.and(in(ID_PROPERTY, assetIds));
+ return findListByStatementAsync(query);
+ }
+
+ @Override
+ public List findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
+ log.debug("Try to find assets by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
+ List assetEntities = findPageWithTextSearch(ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
+ Arrays.asList(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
+ eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
+ pageLink);
+
+ log.trace("Found assets [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", assetEntities, tenantId, customerId, pageLink);
+ return assetEntities;
+ }
+
+ @Override
+ public ListenableFuture> findAssetsByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List assetIds) {
+ log.debug("Try to find assets by tenantId [{}], customerId [{}] and asset Ids [{}]", tenantId, customerId, assetIds);
+ Select select = select().from(getColumnFamilyName());
+ Select.Where query = select.where();
+ query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
+ query.and(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId));
+ query.and(in(ID_PROPERTY, assetIds));
+ return findListByStatementAsync(query);
+ }
+
+ @Override
+ public Optional findAssetsByTenantIdAndName(UUID tenantId, String assetName) {
+ Select select = select().from(ASSET_BY_TENANT_AND_NAME_VIEW_NAME);
+ Select.Where query = select.where();
+ query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
+ query.and(eq(ASSET_NAME_PROPERTY, assetName));
+ return Optional.ofNullable(findOneByStatement(query));
+ }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetSearchQuery.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetSearchQuery.java
new file mode 100644
index 0000000000..f3c69b23bc
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetSearchQuery.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright © 2016-2017 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.dao.asset;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.dao.relation.RelationsSearchParameters;
+import org.thingsboard.server.dao.relation.EntityRelationsQuery;
+import org.thingsboard.server.dao.relation.EntityTypeFilter;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Created by ashvayka on 03.05.17.
+ */
+@Data
+public class AssetSearchQuery {
+
+ private RelationsSearchParameters parameters;
+ @Nullable
+ private String relationType;
+ @Nullable
+ private List assetTypes;
+
+ public EntityRelationsQuery toEntitySearchQuery() {
+ EntityRelationsQuery query = new EntityRelationsQuery();
+ query.setParameters(parameters);
+ query.setFilters(
+ Collections.singletonList(new EntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType,
+ Collections.singletonList(EntityType.ASSET))));
+ return query;
+ }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
new file mode 100644
index 0000000000..7a61bd8019
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright © 2016-2017 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.dao.asset;
+
+import com.google.common.util.concurrent.ListenableFuture;
+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.TenantId;
+import org.thingsboard.server.common.data.page.TextPageData;
+import org.thingsboard.server.common.data.page.TextPageLink;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface AssetService {
+
+ Asset findAssetById(AssetId assetId);
+
+ ListenableFuture findAssetByIdAsync(AssetId assetId);
+
+ Optional findAssetByTenantIdAndName(TenantId tenantId, String name);
+
+ Asset saveAsset(Asset device);
+
+ Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId);
+
+ Asset unassignAssetFromCustomer(AssetId assetId);
+
+ void deleteAsset(AssetId assetId);
+
+ TextPageData findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink);
+
+ ListenableFuture> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List assetIds);
+
+ void deleteAssetsByTenantId(TenantId tenantId);
+
+ TextPageData findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
+
+ ListenableFuture> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List assetIds);
+
+ void unassignCustomerAssets(TenantId tenantId, CustomerId customerId);
+
+ ListenableFuture> findAssetsByQuery(AssetSearchQuery query);
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetTypeFilter.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetTypeFilter.java
new file mode 100644
index 0000000000..221e4df4fc
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetTypeFilter.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2016-2017 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.dao.asset;
+
+import lombok.Data;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+/**
+ * Created by ashvayka on 02.05.17.
+ */
+@Data
+public class AssetTypeFilter {
+ @Nullable
+ private String relationType;
+ @Nullable
+ private List assetTypes;
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
new file mode 100644
index 0000000000..c63e5d7aa0
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
@@ -0,0 +1,292 @@
+/**
+ * Copyright © 2016-2017 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.dao.asset;
+
+
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.data.EntityType;
+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.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.page.TextPageData;
+import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.dao.customer.CustomerDao;
+import org.thingsboard.server.dao.entity.BaseEntityService;
+import org.thingsboard.server.dao.exception.DataValidationException;
+import org.thingsboard.server.dao.model.AssetEntity;
+import org.thingsboard.server.dao.model.CustomerEntity;
+import org.thingsboard.server.dao.model.TenantEntity;
+import org.thingsboard.server.dao.relation.EntityRelationsQuery;
+import org.thingsboard.server.dao.relation.EntitySearchDirection;
+import org.thingsboard.server.dao.service.DataValidator;
+import org.thingsboard.server.dao.service.PaginatedRemover;
+import org.thingsboard.server.dao.tenant.TenantDao;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static org.thingsboard.server.dao.DaoUtil.*;
+import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
+import static org.thingsboard.server.dao.service.Validator.*;
+
+@Service
+@Slf4j
+public class BaseAssetService extends BaseEntityService implements AssetService {
+
+ @Autowired
+ private AssetDao assetDao;
+
+ @Autowired
+ private TenantDao tenantDao;
+
+ @Autowired
+ private CustomerDao customerDao;
+
+ @Override
+ public Asset findAssetById(AssetId assetId) {
+ log.trace("Executing findAssetById [{}]", assetId);
+ validateId(assetId, "Incorrect assetId " + assetId);
+ AssetEntity assetEntity = assetDao.findById(assetId.getId());
+ return getData(assetEntity);
+ }
+
+ @Override
+ public ListenableFuture findAssetByIdAsync(AssetId assetId) {
+ log.trace("Executing findAssetById [{}]", assetId);
+ validateId(assetId, "Incorrect assetId " + assetId);
+ ListenableFuture assetEntity = assetDao.findByIdAsync(assetId.getId());
+ return Futures.transform(assetEntity, (Function super AssetEntity, ? extends Asset>) input -> getData(input));
+ }
+
+ @Override
+ public Optional findAssetByTenantIdAndName(TenantId tenantId, String name) {
+ log.trace("Executing findAssetByTenantIdAndName [{}][{}]", tenantId, name);
+ validateId(tenantId, "Incorrect tenantId " + tenantId);
+ Optional assetEntityOpt = assetDao.findAssetsByTenantIdAndName(tenantId.getId(), name);
+ if (assetEntityOpt.isPresent()) {
+ return Optional.of(getData(assetEntityOpt.get()));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public Asset saveAsset(Asset asset) {
+ log.trace("Executing saveAsset [{}]", asset);
+ assetValidator.validate(asset);
+ return getData(assetDao.save(asset));
+ }
+
+ @Override
+ public Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId) {
+ Asset asset = findAssetById(assetId);
+ asset.setCustomerId(customerId);
+ return saveAsset(asset);
+ }
+
+ @Override
+ public Asset unassignAssetFromCustomer(AssetId assetId) {
+ Asset asset = findAssetById(assetId);
+ asset.setCustomerId(null);
+ return saveAsset(asset);
+ }
+
+ @Override
+ public void deleteAsset(AssetId assetId) {
+ log.trace("Executing deleteAsset [{}]", assetId);
+ validateId(assetId, "Incorrect assetId " + assetId);
+ deleteEntityRelations(assetId);
+ assetDao.removeById(assetId.getId());
+ }
+
+ @Override
+ public TextPageData findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink) {
+ log.trace("Executing findAssetsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
+ validateId(tenantId, "Incorrect tenantId " + tenantId);
+ validatePageLink(pageLink, "Incorrect page link " + pageLink);
+ List assetEntities = assetDao.findAssetsByTenantId(tenantId.getId(), pageLink);
+ List assets = convertDataList(assetEntities);
+ return new TextPageData(assets, pageLink);
+ }
+
+ @Override
+ public ListenableFuture> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List assetIds) {
+ log.trace("Executing findAssetsByTenantIdAndIdsAsync, tenantId [{}], assetIds [{}]", tenantId, assetIds);
+ validateId(tenantId, "Incorrect tenantId " + tenantId);
+ validateIds(assetIds, "Incorrect assetIds " + assetIds);
+ ListenableFuture> assetEntities = assetDao.findAssetsByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(assetIds));
+ return Futures.transform(assetEntities, (Function, List>) input -> convertDataList(input));
+ }
+
+ @Override
+ public void deleteAssetsByTenantId(TenantId tenantId) {
+ log.trace("Executing deleteAssetsByTenantId, tenantId [{}]", tenantId);
+ validateId(tenantId, "Incorrect tenantId " + tenantId);
+ tenantAssetsRemover.removeEntitites(tenantId);
+ }
+
+ @Override
+ public TextPageData findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink) {
+ log.trace("Executing findAssetsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
+ validateId(tenantId, "Incorrect tenantId " + tenantId);
+ validateId(customerId, "Incorrect customerId " + customerId);
+ validatePageLink(pageLink, "Incorrect page link " + pageLink);
+ List assetEntities = assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
+ List assets = convertDataList(assetEntities);
+ return new TextPageData(assets, pageLink);
+ }
+
+ @Override
+ public ListenableFuture> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List assetIds) {
+ log.trace("Executing findAssetsByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], assetIds [{}]", tenantId, customerId, assetIds);
+ validateId(tenantId, "Incorrect tenantId " + tenantId);
+ validateId(customerId, "Incorrect customerId " + customerId);
+ validateIds(assetIds, "Incorrect assetIds " + assetIds);
+ ListenableFuture> assetEntities = assetDao.findAssetsByTenantIdCustomerIdAndIdsAsync(tenantId.getId(),
+ customerId.getId(), toUUIDs(assetIds));
+ return Futures.transform(assetEntities, (Function, List>) input -> convertDataList(input));
+ }
+
+ @Override
+ public void unassignCustomerAssets(TenantId tenantId, CustomerId customerId) {
+ log.trace("Executing unassignCustomerAssets, tenantId [{}], customerId [{}]", tenantId, customerId);
+ validateId(tenantId, "Incorrect tenantId " + tenantId);
+ validateId(customerId, "Incorrect customerId " + customerId);
+ new CustomerAssetsUnassigner(tenantId).removeEntitites(customerId);
+ }
+
+ @Override
+ public ListenableFuture> findAssetsByQuery(AssetSearchQuery query) {
+ ListenableFuture> relations = relationService.findByQuery(query.toEntitySearchQuery());
+ ListenableFuture> assets = Futures.transform(relations, (AsyncFunction, List>) relations1 -> {
+ EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
+ List> futures = new ArrayList<>();
+ for (EntityRelation relation : relations1) {
+ EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
+ if (entityId.getEntityType() == EntityType.ASSET) {
+ futures.add(findAssetByIdAsync(new AssetId(entityId.getId())));
+ }
+ }
+ return Futures.successfulAsList(futures);
+ });
+
+ assets = Futures.transform(assets, new Function, List>() {
+ @Nullable
+ @Override
+ public List apply(@Nullable List assetList) {
+ return assetList.stream().filter(asset -> query.getAssetTypes().contains(asset.getType())).collect(Collectors.toList());
+ }
+ });
+
+ return assets;
+ }
+
+ private DataValidator assetValidator =
+ new DataValidator() {
+
+ @Override
+ protected void validateCreate(Asset asset) {
+ assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
+ d -> {
+ throw new DataValidationException("Asset with such name already exists!");
+ }
+ );
+ }
+
+ @Override
+ protected void validateUpdate(Asset asset) {
+ assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
+ d -> {
+ if (!d.getId().equals(asset.getUuidId())) {
+ throw new DataValidationException("Asset with such name already exists!");
+ }
+ }
+ );
+ }
+
+ @Override
+ protected void validateDataImpl(Asset asset) {
+ if (StringUtils.isEmpty(asset.getName())) {
+ throw new DataValidationException("Asset name should be specified!");
+ }
+ if (asset.getTenantId() == null) {
+ throw new DataValidationException("Asset should be assigned to tenant!");
+ } else {
+ TenantEntity tenant = tenantDao.findById(asset.getTenantId().getId());
+ if (tenant == null) {
+ throw new DataValidationException("Asset is referencing to non-existent tenant!");
+ }
+ }
+ if (asset.getCustomerId() == null) {
+ asset.setCustomerId(new CustomerId(NULL_UUID));
+ } else if (!asset.getCustomerId().getId().equals(NULL_UUID)) {
+ CustomerEntity customer = customerDao.findById(asset.getCustomerId().getId());
+ if (customer == null) {
+ throw new DataValidationException("Can't assign asset to non-existent customer!");
+ }
+ if (!customer.getTenantId().equals(asset.getTenantId().getId())) {
+ throw new DataValidationException("Can't assign asset to customer from different tenant!");
+ }
+ }
+ }
+ };
+
+ private PaginatedRemover tenantAssetsRemover =
+ new PaginatedRemover() {
+
+ @Override
+ protected List findEntities(TenantId id, TextPageLink pageLink) {
+ return assetDao.findAssetsByTenantId(id.getId(), pageLink);
+ }
+
+ @Override
+ protected void removeEntity(AssetEntity entity) {
+ deleteAsset(new AssetId(entity.getId()));
+ }
+ };
+
+ class CustomerAssetsUnassigner extends PaginatedRemover {
+
+ private TenantId tenantId;
+
+ CustomerAssetsUnassigner(TenantId tenantId) {
+ this.tenantId = tenantId;
+ }
+
+ @Override
+ protected List findEntities(CustomerId id, TextPageLink pageLink) {
+ return assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
+ }
+
+ @Override
+ protected void removeEntity(AssetEntity entity) {
+ unassignAssetFromCustomer(new AssetId(entity.getId()));
+ }
+ }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java
index 01f4f99995..e8b42c2c34 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.server.dao.customer;
+import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
@@ -23,16 +24,18 @@ import org.thingsboard.server.common.data.page.TextPageLink;
public interface CustomerService {
- public Customer findCustomerById(CustomerId customerId);
+ Customer findCustomerById(CustomerId customerId);
+
+ ListenableFuture findCustomerByIdAsync(CustomerId customerId);
- public Customer saveCustomer(Customer customer);
+ Customer saveCustomer(Customer customer);
- public void deleteCustomer(CustomerId customerId);
+ void deleteCustomer(CustomerId customerId);
- public Customer findOrCreatePublicCustomer(TenantId tenantId);
-
- public TextPageData findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink);
+ Customer findOrCreatePublicCustomer(TenantId tenantId);
- public void deleteCustomersByTenantId(TenantId tenantId);
+ TextPageData findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink);
+ void deleteCustomersByTenantId(TenantId tenantId);
+
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
index a80c6e84b2..e9f6836774 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.customer;
import static org.thingsboard.server.dao.DaoUtil.convertDataList;
import static org.thingsboard.server.dao.DaoUtil.getData;
+import static org.thingsboard.server.dao.service.Validator.validateId;
import java.io.IOException;
import java.util.List;
@@ -24,31 +25,35 @@ import java.util.Optional;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
+import org.thingsboard.server.dao.model.AssetEntity;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.thingsboard.server.dao.model.TenantEntity;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao;
import org.thingsboard.server.dao.user.UserService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.service.Validator;
@Service
@Slf4j
-public class CustomerServiceImpl implements CustomerService {
+public class CustomerServiceImpl extends BaseEntityService implements CustomerService {
private static final String PUBLIC_CUSTOMER_TITLE = "Public";
@@ -75,6 +80,14 @@ public class CustomerServiceImpl implements CustomerService {
return getData(customerEntity);
}
+ @Override
+ public ListenableFuture findCustomerByIdAsync(CustomerId customerId) {
+ log.trace("Executing findCustomerByIdAsync [{}]", customerId);
+ validateId(customerId, "Incorrect customerId " + customerId);
+ ListenableFuture customerEntity = customerDao.findByIdAsync(customerId.getId());
+ return Futures.transform(customerEntity, (Function super CustomerEntity, ? extends Customer>) input -> getData(input));
+ }
+
@Override
public Customer saveCustomer(Customer customer) {
log.trace("Executing saveCustomer [{}]", customer);
@@ -93,7 +106,8 @@ public class CustomerServiceImpl implements CustomerService {
}
dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId);
deviceService.unassignCustomerDevices(customer.getTenantId(), customerId);
- userService.deleteCustomerUsers(customer.getTenantId(), customerId);
+ userService.deleteCustomerUsers(customer.getTenantId(), customerId);
+ deleteEntityRelations(customerId);
customerDao.removeById(customerId.getId());
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
index 8c860646bd..b0ebbfd5f0 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
@@ -26,7 +26,9 @@ import org.thingsboard.server.common.data.page.TextPageLink;
public interface DashboardService {
public Dashboard findDashboardById(DashboardId dashboardId);
-
+
+ public DashboardInfo findDashboardInfoById(DashboardId dashboardId);
+
public Dashboard saveDashboard(Dashboard dashboard);
public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId);
diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
index 2e0abfbdfd..cf554f3a62 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
@@ -30,20 +30,19 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.customer.CustomerDao;
+import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.*;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.service.Validator;
@Service
@Slf4j
-public class DashboardServiceImpl implements DashboardService {
+public class DashboardServiceImpl extends BaseEntityService implements DashboardService {
@Autowired
private DashboardDao dashboardDao;
@@ -65,6 +64,14 @@ public class DashboardServiceImpl implements DashboardService {
return getData(dashboardEntity);
}
+ @Override
+ public DashboardInfo findDashboardInfoById(DashboardId dashboardId) {
+ log.trace("Executing findDashboardInfoById [{}]", dashboardId);
+ Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
+ DashboardInfoEntity dashboardInfoEntity = dashboardInfoDao.findById(dashboardId.getId());
+ return getData(dashboardInfoEntity);
+ }
+
@Override
public Dashboard saveDashboard(Dashboard dashboard) {
log.trace("Executing saveDashboard [{}]", dashboard);
@@ -91,6 +98,7 @@ public class DashboardServiceImpl implements DashboardService {
public void deleteDashboard(DashboardId dashboardId) {
log.trace("Executing deleteDashboard [{}]", dashboardId);
Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
+ deleteEntityRelations(dashboardId);
dashboardDao.removeById(dashboardId.getId());
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceSearchQuery.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceSearchQuery.java
new file mode 100644
index 0000000000..eb9d9dee3d
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceSearchQuery.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright © 2016-2017 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.dao.device;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.dao.relation.RelationsSearchParameters;
+import org.thingsboard.server.dao.relation.EntityRelationsQuery;
+import org.thingsboard.server.dao.relation.EntityTypeFilter;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.List;
+
+@Data
+public class DeviceSearchQuery {
+
+ private RelationsSearchParameters parameters;
+ @Nullable
+ private String relationType;
+ @Nullable
+ private List deviceTypes;
+
+ public EntityRelationsQuery toEntitySearchQuery() {
+ EntityRelationsQuery query = new EntityRelationsQuery();
+ query.setParameters(parameters);
+ query.setFilters(
+ Collections.singletonList(new EntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType,
+ Collections.singletonList(EntityType.DEVICE))));
+ return query;
+ }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
index 35d34968cf..4715435dec 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
@@ -53,4 +53,7 @@ public interface DeviceService {
ListenableFuture> findDevicesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List deviceIds);
void unassignCustomerDevices(TenantId tenantId, CustomerId customerId);
+
+ ListenableFuture> findDevicesByQuery(DeviceSearchQuery query);
+
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
index 3d1ce31349..ab6fa4e7fa 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.dao.device;
import com.google.common.base.Function;
+import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
@@ -24,36 +25,40 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.customer.CustomerDao;
+import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.thingsboard.server.dao.model.DeviceEntity;
import org.thingsboard.server.dao.model.TenantEntity;
+import org.thingsboard.server.dao.relation.EntitySearchDirection;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao;
+import javax.annotation.Nullable;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
-import static org.thingsboard.server.dao.DaoUtil.convertDataList;
-import static org.thingsboard.server.dao.DaoUtil.getData;
-import static org.thingsboard.server.dao.DaoUtil.toUUIDs;
+import static org.thingsboard.server.dao.DaoUtil.*;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
-import static org.thingsboard.server.dao.service.Validator.validateId;
-import static org.thingsboard.server.dao.service.Validator.validateIds;
-import static org.thingsboard.server.dao.service.Validator.validatePageLink;
+import static org.thingsboard.server.dao.service.Validator.*;
@Service
@Slf4j
-public class DeviceServiceImpl implements DeviceService {
+public class DeviceServiceImpl extends BaseEntityService implements DeviceService {
@Autowired
private DeviceDao deviceDao;
@@ -132,6 +137,7 @@ public class DeviceServiceImpl implements DeviceService {
if (deviceCredentials != null) {
deviceCredentialsService.deleteDeviceCredentials(deviceCredentials);
}
+ deleteEntityRelations(deviceId);
deviceDao.removeById(deviceId.getId());
}
@@ -192,6 +198,32 @@ public class DeviceServiceImpl implements DeviceService {
new CustomerDevicesUnassigner(tenantId).removeEntitites(customerId);
}
+ @Override
+ public ListenableFuture> findDevicesByQuery(DeviceSearchQuery query) {
+ ListenableFuture> relations = relationService.findByQuery(query.toEntitySearchQuery());
+ ListenableFuture> devices = Futures.transform(relations, (AsyncFunction, List>) relations1 -> {
+ EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
+ List> futures = new ArrayList<>();
+ for (EntityRelation relation : relations1) {
+ EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
+ if (entityId.getEntityType() == EntityType.DEVICE) {
+ futures.add(findDeviceByIdAsync(new DeviceId(entityId.getId())));
+ }
+ }
+ return Futures.successfulAsList(futures);
+ });
+
+ devices = Futures.transform(devices, new Function, List>() {
+ @Nullable
+ @Override
+ public List apply(@Nullable List deviceList) {
+ return deviceList.stream().filter(device -> query.getDeviceTypes().contains(device.getType())).collect(Collectors.toList());
+ }
+ });
+
+ return devices;
+ }
+
private DataValidator deviceValidator =
new DataValidator() {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java
new file mode 100644
index 0000000000..3c1c528193
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright © 2016-2017 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.dao.entity;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.dao.relation.RelationService;
+
+/**
+ * Created by ashvayka on 04.05.17.
+ */
+@Slf4j
+public class BaseEntityService {
+
+ @Autowired
+ protected RelationService relationService;
+
+ protected void deleteEntityRelations(EntityId entityId) {
+ log.trace("Executing deleteEntityRelations [{}]", entityId);
+ relationService.deleteEntityRelations(entityId);
+ }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java
new file mode 100644
index 0000000000..0444d11af9
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java
@@ -0,0 +1,235 @@
+/**
+ * Copyright © 2016-2017 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.dao.model;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+import com.datastax.driver.mapping.annotations.Transient;
+import com.fasterxml.jackson.databind.JsonNode;
+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.TenantId;
+import org.thingsboard.server.dao.model.type.JsonCodec;
+
+import java.util.UUID;
+
+import static org.thingsboard.server.dao.model.ModelConstants.*;
+
+@Table(name = ASSET_COLUMN_FAMILY_NAME)
+public final class AssetEntity implements SearchTextEntity {
+
+ @Transient
+ private static final long serialVersionUID = -1265181166886910152L;
+
+ @PartitionKey(value = 0)
+ @Column(name = ID_PROPERTY)
+ private UUID id;
+
+ @PartitionKey(value = 1)
+ @Column(name = ASSET_TENANT_ID_PROPERTY)
+ private UUID tenantId;
+
+ @PartitionKey(value = 2)
+ @Column(name = ASSET_CUSTOMER_ID_PROPERTY)
+ private UUID customerId;
+
+ @Column(name = ASSET_NAME_PROPERTY)
+ private String name;
+
+ @Column(name = ASSET_TYPE_PROPERTY)
+ private String type;
+
+ @Column(name = SEARCH_TEXT_PROPERTY)
+ private String searchText;
+
+ @Column(name = ASSET_ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class)
+ private JsonNode additionalInfo;
+
+ public AssetEntity() {
+ super();
+ }
+
+ public AssetEntity(Asset asset) {
+ if (asset.getId() != null) {
+ this.id = asset.getId().getId();
+ }
+ if (asset.getTenantId() != null) {
+ this.tenantId = asset.getTenantId().getId();
+ }
+ if (asset.getCustomerId() != null) {
+ this.customerId = asset.getCustomerId().getId();
+ }
+ this.name = asset.getName();
+ this.type = asset.getType();
+ this.additionalInfo = asset.getAdditionalInfo();
+ }
+
+ public UUID getId() {
+ return id;
+ }
+
+ public void setId(UUID id) {
+ this.id = id;
+ }
+
+ public UUID getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(UUID tenantId) {
+ this.tenantId = tenantId;
+ }
+
+ public UUID getCustomerId() {
+ return customerId;
+ }
+
+ public void setCustomerId(UUID customerId) {
+ this.customerId = customerId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public JsonNode getAdditionalInfo() {
+ return additionalInfo;
+ }
+
+ public void setAdditionalInfo(JsonNode additionalInfo) {
+ this.additionalInfo = additionalInfo;
+ }
+
+ @Override
+ public String getSearchTextSource() {
+ return name;
+ }
+
+ @Override
+ public void setSearchText(String searchText) {
+ this.searchText = searchText;
+ }
+
+ public String getSearchText() {
+ return searchText;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode());
+ result = prime * result + ((customerId == null) ? 0 : customerId.hashCode());
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AssetEntity other = (AssetEntity) obj;
+ if (additionalInfo == null) {
+ if (other.additionalInfo != null)
+ return false;
+ } else if (!additionalInfo.equals(other.additionalInfo))
+ return false;
+ if (customerId == null) {
+ if (other.customerId != null)
+ return false;
+ } else if (!customerId.equals(other.customerId))
+ return false;
+ if (id == null) {
+ if (other.id != null)
+ return false;
+ } else if (!id.equals(other.id))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ } else if (!type.equals(other.type))
+ return false;
+ if (tenantId == null) {
+ if (other.tenantId != null)
+ return false;
+ } else if (!tenantId.equals(other.tenantId))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("AssetEntity [id=");
+ builder.append(id);
+ builder.append(", tenantId=");
+ builder.append(tenantId);
+ builder.append(", customerId=");
+ builder.append(customerId);
+ builder.append(", name=");
+ builder.append(name);
+ builder.append(", type=");
+ builder.append(type);
+ builder.append(", additionalInfo=");
+ builder.append(additionalInfo);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ @Override
+ public Asset toData() {
+ Asset asset = new Asset(new AssetId(id));
+ asset.setCreatedTime(UUIDs.unixTimestamp(id));
+ if (tenantId != null) {
+ asset.setTenantId(new TenantId(tenantId));
+ }
+ if (customerId != null) {
+ asset.setCustomerId(new CustomerId(customerId));
+ }
+ asset.setName(name);
+ asset.setType(type);
+ asset.setAdditionalInfo(additionalInfo);
+ return asset;
+ }
+
+}
\ No newline at end of file
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java
index 740bf2ac44..69ed92cc1c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java
@@ -51,7 +51,10 @@ public final class DeviceEntity implements SearchTextEntity {
@Column(name = DEVICE_NAME_PROPERTY)
private String name;
-
+
+ @Column(name = DEVICE_TYPE_PROPERTY)
+ private String type;
+
@Column(name = SEARCH_TEXT_PROPERTY)
private String searchText;
@@ -73,6 +76,7 @@ public final class DeviceEntity implements SearchTextEntity {
this.customerId = device.getCustomerId().getId();
}
this.name = device.getName();
+ this.type = device.getType();
this.additionalInfo = device.getAdditionalInfo();
}
@@ -108,6 +112,14 @@ public final class DeviceEntity implements SearchTextEntity {
this.name = name;
}
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
public JsonNode getAdditionalInfo() {
return additionalInfo;
}
@@ -138,6 +150,7 @@ public final class DeviceEntity implements SearchTextEntity {
result = prime * result + ((customerId == null) ? 0 : customerId.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
return result;
}
@@ -171,6 +184,11 @@ public final class DeviceEntity implements SearchTextEntity {
return false;
} else if (!name.equals(other.name))
return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ } else if (!type.equals(other.type))
+ return false;
if (tenantId == null) {
if (other.tenantId != null)
return false;
@@ -190,6 +208,8 @@ public final class DeviceEntity implements SearchTextEntity {
builder.append(customerId);
builder.append(", name=");
builder.append(name);
+ builder.append(", type=");
+ builder.append(type);
builder.append(", additionalInfo=");
builder.append(additionalInfo);
builder.append("]");
@@ -207,6 +227,7 @@ public final class DeviceEntity implements SearchTextEntity {
device.setCustomerId(new CustomerId(customerId));
}
device.setName(name);
+ device.setType(type);
device.setAdditionalInfo(additionalInfo);
return device;
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/EventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/EventEntity.java
index fc22a8dbe7..78928146cb 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/EventEntity.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/EventEntity.java
@@ -35,7 +35,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.*;
*/
@Data
@NoArgsConstructor
-@Table(name = DEVICE_COLUMN_FAMILY_NAME)
+@Table(name = EVENT_COLUMN_FAMILY_NAME)
public class EventEntity implements BaseEntity {
@Transient
@@ -98,23 +98,7 @@ public class EventEntity implements BaseEntity {
Event event = new Event(new EventId(id));
event.setCreatedTime(UUIDs.unixTimestamp(id));
event.setTenantId(new TenantId(tenantId));
- switch (entityType) {
- case TENANT:
- event.setEntityId(new TenantId(entityId));
- break;
- case DEVICE:
- event.setEntityId(new DeviceId(entityId));
- break;
- case CUSTOMER:
- event.setEntityId(new CustomerId(entityId));
- break;
- case RULE:
- event.setEntityId(new RuleId(entityId));
- break;
- case PLUGIN:
- event.setEntityId(new PluginId(entityId));
- break;
- }
+ event.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId));
event.setBody(body);
event.setType(eventType);
event.setUid(eventUId);
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
index 9c9c66b376..9f78e27557 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
@@ -120,12 +120,39 @@ public class ModelConstants {
public static final String DEVICE_TENANT_ID_PROPERTY = TENTANT_ID_PROPERTY;
public static final String DEVICE_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
public static final String DEVICE_NAME_PROPERTY = "name";
+ public static final String DEVICE_TYPE_PROPERTY = "type";
public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text";
public static final String DEVICE_BY_TENANT_AND_NAME_VIEW_NAME = "device_by_tenant_and_name";
+ /**
+ * Cassandra asset constants.
+ */
+ public static final String ASSET_COLUMN_FAMILY_NAME = "asset";
+ public static final String ASSET_TENANT_ID_PROPERTY = TENTANT_ID_PROPERTY;
+ public static final String ASSET_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
+ public static final String ASSET_NAME_PROPERTY = "name";
+ public static final String ASSET_TYPE_PROPERTY = "type";
+ public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
+
+ public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text";
+ public static final String ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_and_search_text";
+ public static final String ASSET_BY_TENANT_AND_NAME_VIEW_NAME = "asset_by_tenant_and_name";
+
+ /**
+ * Cassandra entity relation constants.
+ */
+ public static final String RELATION_COLUMN_FAMILY_NAME = "relation";
+ public static final String RELATION_FROM_ID_PROPERTY = "from_id";
+ public static final String RELATION_FROM_TYPE_PROPERTY = "from_type";
+ public static final String RELATION_TO_ID_PROPERTY = "to_id";
+ public static final String RELATION_TO_TYPE_PROPERTY = "to_type";
+ public static final String RELATION_TYPE_PROPERTY = "relation_type";
+
+ public static final String RELATION_REVERSE_VIEW_NAME = "reverse_relation";
+
/**
* Cassandra device_credentials constants.
diff --git a/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java b/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java
index 99560d2b8a..5e0eb399f6 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java
@@ -15,13 +15,14 @@
*/
package org.thingsboard.server.dao.plugin;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.PluginId;
+import org.thingsboard.server.common.data.id.RuleId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
@@ -29,7 +30,9 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
+import org.thingsboard.server.common.data.rule.RuleMetaData;
import org.thingsboard.server.dao.component.ComponentDescriptorService;
+import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DatabaseException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
@@ -48,10 +51,11 @@ import java.util.stream.Collectors;
import static org.thingsboard.server.dao.DaoUtil.convertDataList;
import static org.thingsboard.server.dao.DaoUtil.getData;
+import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@Slf4j
-public class BasePluginService implements PluginService {
+public class BasePluginService extends BaseEntityService implements PluginService {
//TODO: move to a better place.
public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
@@ -108,6 +112,13 @@ public class BasePluginService implements PluginService {
return getData(pluginDao.findById(pluginId));
}
+ @Override
+ public ListenableFuture findPluginByIdAsync(PluginId pluginId) {
+ validateId(pluginId, "Incorrect plugin id for search plugin request.");
+ ListenableFuture pluginEntity = pluginDao.findByIdAsync(pluginId.getId());
+ return Futures.transform(pluginEntity, (com.google.common.base.Function super PluginMetaDataEntity, ? extends PluginMetaData>) input -> getData(input));
+ }
+
@Override
public PluginMetaData findPluginByApiToken(String apiToken) {
Validator.validateString(apiToken, "Incorrect plugin apiToken for search request.");
@@ -205,6 +216,7 @@ public class BasePluginService implements PluginService {
@Override
public void deletePluginById(PluginId pluginId) {
Validator.validateId(pluginId, "Incorrect plugin id for delete request.");
+ deleteEntityRelations(pluginId);
checkRulesAndDelete(pluginId.getId());
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/plugin/PluginService.java b/dao/src/main/java/org/thingsboard/server/dao/plugin/PluginService.java
index f8173ff39a..371a46e0fa 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/plugin/PluginService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/plugin/PluginService.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.server.dao.plugin;
+import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
@@ -29,6 +30,8 @@ public interface PluginService {
PluginMetaData findPluginById(PluginId pluginId);
+ ListenableFuture findPluginByIdAsync(PluginId pluginId);
+
PluginMetaData findPluginByApiToken(String apiToken);
TextPageData findSystemPlugins(TextPageLink pageLink);
diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java
new file mode 100644
index 0000000000..5fd663212c
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java
@@ -0,0 +1,279 @@
+/**
+ * Copyright © 2016-2017 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.dao.relation;
+
+import com.datastax.driver.core.*;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.EntityIdFactory;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.dao.AbstractAsyncDao;
+import org.thingsboard.server.dao.model.ModelConstants;
+
+import javax.annotation.Nullable;
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by ashvayka on 25.04.17.
+ */
+@Component
+@Slf4j
+public class BaseRelationDao extends AbstractAsyncDao implements RelationDao {
+
+ private static final String SELECT_COLUMNS = "SELECT " +
+ ModelConstants.RELATION_FROM_ID_PROPERTY + "," +
+ ModelConstants.RELATION_FROM_TYPE_PROPERTY + "," +
+ ModelConstants.RELATION_TO_ID_PROPERTY + "," +
+ ModelConstants.RELATION_TO_TYPE_PROPERTY + "," +
+ ModelConstants.RELATION_TYPE_PROPERTY + "," +
+ ModelConstants.ADDITIONAL_INFO_PROPERTY;
+ public static final String FROM = " FROM ";
+ public static final String WHERE = " WHERE ";
+ public static final String AND = " AND ";
+
+ private PreparedStatement saveStmt;
+ private PreparedStatement findAllByFromStmt;
+ private PreparedStatement findAllByFromAndTypeStmt;
+ private PreparedStatement findAllByToStmt;
+ private PreparedStatement findAllByToAndTypeStmt;
+ private PreparedStatement checkRelationStmt;
+ private PreparedStatement deleteStmt;
+ private PreparedStatement deleteAllByEntityStmt;
+
+ @PostConstruct
+ public void init() {
+ super.startExecutor();
+ }
+
+ @Override
+ public ListenableFuture> findAllByFrom(EntityId from) {
+ BoundStatement stmt = getFindAllByFromStmt().bind().setUUID(0, from.getId()).setString(1, from.getEntityType().name());
+ return executeAsyncRead(from, stmt);
+ }
+
+ @Override
+ public ListenableFuture> findAllByFromAndType(EntityId from, String relationType) {
+ BoundStatement stmt = getFindAllByFromAndTypeStmt().bind()
+ .setUUID(0, from.getId())
+ .setString(1, from.getEntityType().name())
+ .setString(2, relationType);
+ return executeAsyncRead(from, stmt);
+ }
+
+ @Override
+ public ListenableFuture> findAllByTo(EntityId to) {
+ BoundStatement stmt = getFindAllByToStmt().bind().setUUID(0, to.getId()).setString(1, to.getEntityType().name());
+ return executeAsyncRead(to, stmt);
+ }
+
+ @Override
+ public ListenableFuture> findAllByToAndType(EntityId to, String relationType) {
+ BoundStatement stmt = getFindAllByToAndTypeStmt().bind()
+ .setUUID(0, to.getId())
+ .setString(1, to.getEntityType().name())
+ .setString(2, relationType);
+ return executeAsyncRead(to, stmt);
+ }
+
+ @Override
+ public ListenableFuture checkRelation(EntityId from, EntityId to, String relationType) {
+ BoundStatement stmt = getCheckRelationStmt().bind()
+ .setUUID(0, from.getId())
+ .setString(1, from.getEntityType().name())
+ .setUUID(2, to.getId())
+ .setString(3, to.getEntityType().name())
+ .setString(4, relationType);
+ return getFuture(executeAsyncRead(stmt), rs -> rs != null ? rs.one() != null : false);
+ }
+
+ @Override
+ public ListenableFuture saveRelation(EntityRelation relation) {
+ BoundStatement stmt = getSaveStmt().bind()
+ .setUUID(0, relation.getFrom().getId())
+ .setString(1, relation.getFrom().getEntityType().name())
+ .setUUID(2, relation.getTo().getId())
+ .setString(3, relation.getTo().getEntityType().name())
+ .setString(4, relation.getType())
+ .set(5, relation.getAdditionalInfo(), JsonNode.class);
+ ResultSetFuture future = executeAsyncWrite(stmt);
+ return getBooleanListenableFuture(future);
+ }
+
+ @Override
+ public ListenableFuture deleteRelation(EntityRelation relation) {
+ return deleteRelation(relation.getFrom(), relation.getTo(), relation.getType());
+ }
+
+ @Override
+ public ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType) {
+ BoundStatement stmt = getDeleteStmt().bind()
+ .setUUID(0, from.getId())
+ .setString(1, from.getEntityType().name())
+ .setUUID(2, to.getId())
+ .setString(3, to.getEntityType().name())
+ .setString(4, relationType);
+ ResultSetFuture future = executeAsyncWrite(stmt);
+ return getBooleanListenableFuture(future);
+ }
+
+ @Override
+ public ListenableFuture deleteOutboundRelations(EntityId entity) {
+ BoundStatement stmt = getDeleteAllByEntityStmt().bind()
+ .setUUID(0, entity.getId())
+ .setString(1, entity.getEntityType().name());
+ ResultSetFuture future = executeAsyncWrite(stmt);
+ return getBooleanListenableFuture(future);
+ }
+
+ private PreparedStatement getSaveStmt() {
+ if (saveStmt == null) {
+ saveStmt = getSession().prepare("INSERT INTO " + ModelConstants.RELATION_COLUMN_FAMILY_NAME + " " +
+ "(" + ModelConstants.RELATION_FROM_ID_PROPERTY +
+ "," + ModelConstants.RELATION_FROM_TYPE_PROPERTY +
+ "," + ModelConstants.RELATION_TO_ID_PROPERTY +
+ "," + ModelConstants.RELATION_TO_TYPE_PROPERTY +
+ "," + ModelConstants.RELATION_TYPE_PROPERTY +
+ "," + ModelConstants.ADDITIONAL_INFO_PROPERTY + ")" +
+ " VALUES(?, ?, ?, ?, ?, ?)");
+ }
+ return saveStmt;
+ }
+
+ private PreparedStatement getDeleteStmt() {
+ if (deleteStmt == null) {
+ deleteStmt = getSession().prepare("DELETE FROM " + ModelConstants.RELATION_COLUMN_FAMILY_NAME +
+ WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ?" +
+ AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ?" +
+ AND + ModelConstants.RELATION_TO_ID_PROPERTY + " = ?" +
+ AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ?" +
+ AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ?");
+ }
+ return deleteStmt;
+ }
+
+ private PreparedStatement getDeleteAllByEntityStmt() {
+ if (deleteAllByEntityStmt == null) {
+ deleteAllByEntityStmt = getSession().prepare("DELETE FROM " + ModelConstants.RELATION_COLUMN_FAMILY_NAME +
+ WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ?" +
+ AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ?");
+ }
+ return deleteAllByEntityStmt;
+ }
+
+ private PreparedStatement getFindAllByFromStmt() {
+ if (findAllByFromStmt == null) {
+ findAllByFromStmt = getSession().prepare(SELECT_COLUMNS + " " +
+ FROM + ModelConstants.RELATION_COLUMN_FAMILY_NAME + " " +
+ WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ? " +
+ AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ? ");
+ }
+ return findAllByFromStmt;
+ }
+
+ private PreparedStatement getFindAllByFromAndTypeStmt() {
+ if (findAllByFromAndTypeStmt == null) {
+ findAllByFromAndTypeStmt = getSession().prepare(SELECT_COLUMNS + " " +
+ FROM + ModelConstants.RELATION_COLUMN_FAMILY_NAME + " " +
+ WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ? " +
+ AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ? " +
+ AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ? ");
+ }
+ return findAllByFromAndTypeStmt;
+ }
+
+ private PreparedStatement getFindAllByToStmt() {
+ if (findAllByToStmt == null) {
+ findAllByToStmt = getSession().prepare(SELECT_COLUMNS + " " +
+ FROM + ModelConstants.RELATION_REVERSE_VIEW_NAME + " " +
+ WHERE + ModelConstants.RELATION_TO_ID_PROPERTY + " = ? " +
+ AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ? ");
+ }
+ return findAllByToStmt;
+ }
+
+ private PreparedStatement getFindAllByToAndTypeStmt() {
+ if (findAllByToAndTypeStmt == null) {
+ findAllByToAndTypeStmt = getSession().prepare(SELECT_COLUMNS + " " +
+ FROM + ModelConstants.RELATION_REVERSE_VIEW_NAME + " " +
+ WHERE + ModelConstants.RELATION_TO_ID_PROPERTY + " = ? " +
+ AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ? " +
+ AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ? ");
+ }
+ return findAllByToAndTypeStmt;
+ }
+
+ private PreparedStatement getCheckRelationStmt() {
+ if (checkRelationStmt == null) {
+ checkRelationStmt = getSession().prepare(SELECT_COLUMNS + " " +
+ FROM + ModelConstants.RELATION_COLUMN_FAMILY_NAME + " " +
+ WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ? " +
+ AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ? " +
+ AND + ModelConstants.RELATION_TO_ID_PROPERTY + " = ? " +
+ AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ? " +
+ AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ? ");
+ }
+ return checkRelationStmt;
+ }
+
+ private EntityRelation getEntityRelation(Row row) {
+ EntityRelation relation = new EntityRelation();
+ relation.setType(row.getString(ModelConstants.RELATION_TYPE_PROPERTY));
+ relation.setAdditionalInfo(row.get(ModelConstants.ADDITIONAL_INFO_PROPERTY, JsonNode.class));
+ relation.setFrom(toEntity(row, ModelConstants.RELATION_FROM_ID_PROPERTY, ModelConstants.RELATION_FROM_TYPE_PROPERTY));
+ relation.setTo(toEntity(row, ModelConstants.RELATION_TO_ID_PROPERTY, ModelConstants.RELATION_TO_TYPE_PROPERTY));
+ return relation;
+ }
+
+ private EntityId toEntity(Row row, String uuidColumn, String typeColumn) {
+ return EntityIdFactory.getByTypeAndUuid(row.getString(typeColumn), row.getUUID(uuidColumn));
+ }
+
+ private ListenableFuture> executeAsyncRead(EntityId from, BoundStatement stmt) {
+ log.debug("Generated query [{}] for entity {}", stmt, from);
+ return getFuture(executeAsyncRead(stmt), rs -> {
+ List rows = rs.all();
+ List entries = new ArrayList<>(rows.size());
+ if (!rows.isEmpty()) {
+ rows.forEach(row -> {
+ entries.add(getEntityRelation(row));
+ });
+ }
+ return entries;
+ });
+ }
+
+ private ListenableFuture getBooleanListenableFuture(ResultSetFuture rsFuture) {
+ return getFuture(rsFuture, rs -> rs != null ? rs.wasApplied() : false);
+ }
+
+ private ListenableFuture getFuture(ResultSetFuture future, java.util.function.Function transformer) {
+ return Futures.transform(future, new Function() {
+ @Nullable
+ @Override
+ public T apply(@Nullable ResultSet input) {
+ return transformer.apply(input);
+ }
+ }, readResultsProcessingExecutor);
+ }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
new file mode 100644
index 0000000000..1b572c64ad
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
@@ -0,0 +1,257 @@
+/**
+ * Copyright © 2016-2017 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.dao.relation;
+
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.dao.exception.DataValidationException;
+
+import javax.annotation.Nullable;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Created by ashvayka on 28.04.17.
+ */
+@Service
+@Slf4j
+public class BaseRelationService implements RelationService {
+
+ @Autowired
+ private RelationDao relationDao;
+
+ @Override
+ public ListenableFuture checkRelation(EntityId from, EntityId to, String relationType) {
+ log.trace("Executing checkRelation [{}][{}][{}]", from, to, relationType);
+ validate(from, to, relationType);
+ return relationDao.checkRelation(from, to, relationType);
+ }
+
+ @Override
+ public ListenableFuture saveRelation(EntityRelation relation) {
+ log.trace("Executing saveRelation [{}]", relation);
+ validate(relation);
+ return relationDao.saveRelation(relation);
+ }
+
+ @Override
+ public ListenableFuture deleteRelation(EntityRelation relation) {
+ log.trace("Executing deleteRelation [{}]", relation);
+ validate(relation);
+ return relationDao.deleteRelation(relation);
+ }
+
+ @Override
+ public ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType) {
+ log.trace("Executing deleteRelation [{}][{}][{}]", from, to, relationType);
+ validate(from, to, relationType);
+ return relationDao.deleteRelation(from, to, relationType);
+ }
+
+ @Override
+ public ListenableFuture deleteEntityRelations(EntityId entity) {
+ log.trace("Executing deleteEntityRelations [{}]", entity);
+ validate(entity);
+ ListenableFuture> inboundRelations = relationDao.findAllByTo(entity);
+ ListenableFuture> inboundDeletions = Futures.transform(inboundRelations, new AsyncFunction