Commit 0661de2392cb10035ced56be675eb97e94c41337

Authored by mp-loki
2 parents ed6de776 96dad362

Merge remote-tracking branch 'origin/asset-alarm-mgmt' into dao-refactoring-vs

Showing 75 changed files with 3652 additions and 182 deletions

Too many changes to show.

To preserve performance only 75 of 265 files are displayed.

  1 +*.toDelete
1 2 output/**
2 3 *.class
3 4 *~
... ...
1   -!bin/
\ No newline at end of file
  1 +!bin/
  2 +/bin/
... ...
... ... @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
37 37 import org.thingsboard.server.common.msg.cluster.ServerAddress;
38 38 import org.thingsboard.server.common.transport.auth.DeviceAuthService;
39 39 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
  40 +import org.thingsboard.server.dao.asset.AssetService;
40 41 import org.thingsboard.server.dao.attributes.AttributesService;
41 42 import org.thingsboard.server.dao.customer.CustomerService;
42 43 import org.thingsboard.server.dao.device.DeviceService;
... ... @@ -81,6 +82,9 @@ public class ActorSystemContext {
81 82 @Getter private DeviceService deviceService;
82 83
83 84 @Autowired
  85 + @Getter private AssetService assetService;
  86 +
  87 + @Autowired
84 88 @Getter private TenantService tenantService;
85 89
86 90 @Autowired
... ...
... ... @@ -51,13 +51,7 @@ import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestBody;
51 51 import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg;
52 52 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;
53 53
54   -import java.util.HashMap;
55   -import java.util.HashSet;
56   -import java.util.List;
57   -import java.util.Map;
58   -import java.util.Optional;
59   -import java.util.Set;
60   -import java.util.UUID;
  54 +import java.util.*;
61 55 import java.util.concurrent.ExecutionException;
62 56 import java.util.concurrent.TimeoutException;
63 57 import java.util.function.Consumer;
... ... @@ -205,25 +199,21 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
205 199
206 200 void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
207 201 refreshAttributes(msg);
208   - Set<AttributeKey> keys = msg.getDeletedKeys();
209 202 if (attributeSubscriptions.size() > 0) {
210 203 ToDeviceMsg notification = null;
211 204 if (msg.isDeleted()) {
212   - List<AttributeKey> sharedKeys = keys.stream()
  205 + List<AttributeKey> sharedKeys = msg.getDeletedKeys().stream()
213 206 .filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))
214 207 .collect(Collectors.toList());
215 208 notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromDeleted(sharedKeys));
216 209 } else {
217   - List<AttributeKvEntry> attributes = keys.stream()
218   - .filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))
219   - .map(key -> deviceAttributes.getServerPublicAttribute(key.getAttributeKey()))
220   - .filter(Optional::isPresent)
221   - .map(Optional::get)
222   - .collect(Collectors.toList());
223   - if (attributes.size() > 0) {
224   - notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromShared(attributes));
225   - } else {
226   - logger.debug("[{}] No public server side attributes changed!", deviceId);
  210 + if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {
  211 + List<AttributeKvEntry> attributes = new ArrayList<>(msg.getValues());
  212 + if (attributes.size() > 0) {
  213 + notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromShared(attributes));
  214 + } else {
  215 + logger.debug("[{}] No public server side attributes changed!", deviceId);
  216 + }
227 217 }
228 218 }
229 219 if (notification != null) {
... ...
... ... @@ -24,17 +24,19 @@ import com.google.common.util.concurrent.FutureCallback;
24 24 import com.google.common.util.concurrent.Futures;
25 25 import com.google.common.util.concurrent.ListenableFuture;
26 26 import lombok.extern.slf4j.Slf4j;
27   -import org.thingsboard.server.common.data.DataConstants;
  27 +import org.thingsboard.server.common.data.Customer;
28 28 import org.thingsboard.server.common.data.Device;
29   -import org.thingsboard.server.common.data.id.CustomerId;
30   -import org.thingsboard.server.common.data.id.DeviceId;
31   -import org.thingsboard.server.common.data.id.PluginId;
  29 +import org.thingsboard.server.common.data.EntityType;
  30 +import org.thingsboard.server.common.data.Tenant;
  31 +import org.thingsboard.server.common.data.asset.Asset;
32 32 import org.thingsboard.server.common.data.id.TenantId;
33 33 import org.thingsboard.server.common.data.kv.AttributeKey;
34 34 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
35 35 import org.thingsboard.server.common.data.kv.TsKvEntry;
36 36 import org.thingsboard.server.common.data.kv.TsKvQuery;
37 37 import org.thingsboard.server.common.data.page.TextPageLink;
  38 +import org.thingsboard.server.common.data.plugin.PluginMetaData;
  39 +import org.thingsboard.server.common.data.rule.RuleMetaData;
38 40 import org.thingsboard.server.common.msg.cluster.ServerAddress;
39 41 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
40 42 import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext;
... ... @@ -89,103 +91,107 @@ public final class PluginProcessingContext implements PluginContext {
89 91 }
90 92
91 93 @Override
92   - public void saveAttributes(final TenantId tenantId, final DeviceId deviceId, final String scope, final List<AttributeKvEntry> attributes, final PluginCallback<Void> callback) {
93   - validate(deviceId, new ValidationCallback(callback, ctx -> {
94   - ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.attributesService.save(deviceId, scope, attributes);
  94 + public void saveAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List<AttributeKvEntry> attributes, final PluginCallback<Void> callback) {
  95 + validate(entityId, new ValidationCallback(callback, ctx -> {
  96 + ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.attributesService.save(entityId, scope, attributes);
95 97 Futures.addCallback(rsListFuture, getListCallback(callback, v -> {
96   - onDeviceAttributesChanged(tenantId, deviceId, scope, attributes);
  98 + if (entityId.getEntityType() == EntityType.DEVICE) {
  99 + onDeviceAttributesChanged(tenantId, new DeviceId(entityId.getId()), scope, attributes);
  100 + }
97 101 return null;
98 102 }), executor);
99 103 }));
100 104 }
101 105
102 106 @Override
103   - public void removeAttributes(final TenantId tenantId, final DeviceId deviceId, final String scope, final List<String> keys, final PluginCallback<Void> callback) {
104   - validate(deviceId, new ValidationCallback(callback, ctx -> {
105   - ListenableFuture<List<ResultSet>> future = pluginCtx.attributesService.removeAll(deviceId, scope, keys);
  107 + public void removeAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List<String> keys, final PluginCallback<Void> callback) {
  108 + validate(entityId, new ValidationCallback(callback, ctx -> {
  109 + ListenableFuture<List<ResultSet>> future = pluginCtx.attributesService.removeAll(entityId, scope, keys);
106 110 Futures.addCallback(future, getCallback(callback, v -> null), executor);
107   - onDeviceAttributesDeleted(tenantId, deviceId, keys.stream().map(key -> new AttributeKey(scope, key)).collect(Collectors.toSet()));
  111 + if (entityId.getEntityType() == EntityType.DEVICE) {
  112 + onDeviceAttributesDeleted(tenantId, new DeviceId(entityId.getId()), keys.stream().map(key -> new AttributeKey(scope, key)).collect(Collectors.toSet()));
  113 + }
108 114 }));
109 115 }
110 116
111 117 @Override
112   - public void loadAttribute(DeviceId deviceId, String attributeType, String attributeKey, final PluginCallback<Optional<AttributeKvEntry>> callback) {
113   - validate(deviceId, new ValidationCallback(callback, ctx -> {
114   - ListenableFuture<Optional<AttributeKvEntry>> future = pluginCtx.attributesService.find(deviceId, attributeType, attributeKey);
  118 + public void loadAttribute(EntityId entityId, String attributeType, String attributeKey, final PluginCallback<Optional<AttributeKvEntry>> callback) {
  119 + validate(entityId, new ValidationCallback(callback, ctx -> {
  120 + ListenableFuture<Optional<AttributeKvEntry>> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKey);
115 121 Futures.addCallback(future, getCallback(callback, v -> v), executor);
116 122 }));
117 123 }
118 124
119 125 @Override
120   - public void loadAttributes(DeviceId deviceId, String attributeType, Collection<String> attributeKeys, final PluginCallback<List<AttributeKvEntry>> callback) {
121   - validate(deviceId, new ValidationCallback(callback, ctx -> {
122   - ListenableFuture<List<AttributeKvEntry>> future = pluginCtx.attributesService.find(deviceId, attributeType, attributeKeys);
  126 + public void loadAttributes(EntityId entityId, String attributeType, Collection<String> attributeKeys, final PluginCallback<List<AttributeKvEntry>> callback) {
  127 + validate(entityId, new ValidationCallback(callback, ctx -> {
  128 + ListenableFuture<List<AttributeKvEntry>> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKeys);
123 129 Futures.addCallback(future, getCallback(callback, v -> v), executor);
124 130 }));
125 131 }
126 132
127 133 @Override
128   - public void loadAttributes(DeviceId deviceId, String attributeType, PluginCallback<List<AttributeKvEntry>> callback) {
129   - validate(deviceId, new ValidationCallback(callback, ctx -> {
130   - ListenableFuture<List<AttributeKvEntry>> future = pluginCtx.attributesService.findAll(deviceId, attributeType);
  134 + public void loadAttributes(EntityId entityId, String attributeType, PluginCallback<List<AttributeKvEntry>> callback) {
  135 + validate(entityId, new ValidationCallback(callback, ctx -> {
  136 + ListenableFuture<List<AttributeKvEntry>> future = pluginCtx.attributesService.findAll(entityId, attributeType);
131 137 Futures.addCallback(future, getCallback(callback, v -> v), executor);
132 138 }));
133 139 }
134 140
135 141 @Override
136   - public void loadAttributes(final DeviceId deviceId, final Collection<String> attributeTypes, final PluginCallback<List<AttributeKvEntry>> callback) {
137   - validate(deviceId, new ValidationCallback(callback, ctx -> {
  142 + public void loadAttributes(final EntityId entityId, final Collection<String> attributeTypes, final PluginCallback<List<AttributeKvEntry>> callback) {
  143 + validate(entityId, new ValidationCallback(callback, ctx -> {
138 144 List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
139   - attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.findAll(deviceId, attributeType)));
  145 + attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.findAll(entityId, attributeType)));
140 146 convertFuturesAndAddCallback(callback, futures);
141 147 }));
142 148 }
143 149
144 150 @Override
145   - public void loadAttributes(final DeviceId deviceId, final Collection<String> attributeTypes, final Collection<String> attributeKeys, final PluginCallback<List<AttributeKvEntry>> callback) {
146   - validate(deviceId, new ValidationCallback(callback, ctx -> {
  151 + public void loadAttributes(final EntityId entityId, final Collection<String> attributeTypes, final Collection<String> attributeKeys, final PluginCallback<List<AttributeKvEntry>> callback) {
  152 + validate(entityId, new ValidationCallback(callback, ctx -> {
147 153 List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
148   - attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.find(deviceId, attributeType, attributeKeys)));
  154 + attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.find(entityId, attributeType, attributeKeys)));
149 155 convertFuturesAndAddCallback(callback, futures);
150 156 }));
151 157 }
152 158
153 159 @Override
154   - public void saveTsData(final DeviceId deviceId, final TsKvEntry entry, final PluginCallback<Void> callback) {
155   - validate(deviceId, new ValidationCallback(callback, ctx -> {
156   - ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.save(DataConstants.DEVICE, deviceId, entry);
  160 + public void saveTsData(final EntityId entityId, final TsKvEntry entry, final PluginCallback<Void> callback) {
  161 + validate(entityId, new ValidationCallback(callback, ctx -> {
  162 + ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.save(entityId, entry);
157 163 Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor);
158 164 }));
159 165 }
160 166
161 167 @Override
162   - public void saveTsData(final DeviceId deviceId, final List<TsKvEntry> entries, final PluginCallback<Void> callback) {
163   - validate(deviceId, new ValidationCallback(callback, ctx -> {
164   - ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.save(DataConstants.DEVICE, deviceId, entries);
  168 + public void saveTsData(final EntityId entityId, final List<TsKvEntry> entries, final PluginCallback<Void> callback) {
  169 + validate(entityId, new ValidationCallback(callback, ctx -> {
  170 + ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.save(entityId, entries);
165 171 Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor);
166 172 }));
167 173 }
168 174
169 175 @Override
170   - public void loadTimeseries(final DeviceId deviceId, final List<TsKvQuery> queries, final PluginCallback<List<TsKvEntry>> callback) {
171   - validate(deviceId, new ValidationCallback(callback, ctx -> {
172   - ListenableFuture<List<TsKvEntry>> future = pluginCtx.tsService.findAll(DataConstants.DEVICE, deviceId, queries);
  176 + public void loadTimeseries(final EntityId entityId, final List<TsKvQuery> queries, final PluginCallback<List<TsKvEntry>> callback) {
  177 + validate(entityId, new ValidationCallback(callback, ctx -> {
  178 + ListenableFuture<List<TsKvEntry>> future = pluginCtx.tsService.findAll(entityId, queries);
173 179 Futures.addCallback(future, getCallback(callback, v -> v), executor);
174 180 }));
175 181 }
176 182
177 183 @Override
178   - public void loadLatestTimeseries(final DeviceId deviceId, final PluginCallback<List<TsKvEntry>> callback) {
179   - validate(deviceId, new ValidationCallback(callback, ctx -> {
180   - ResultSetFuture future = pluginCtx.tsService.findAllLatest(DataConstants.DEVICE, deviceId);
  184 + public void loadLatestTimeseries(final EntityId entityId, final PluginCallback<List<TsKvEntry>> callback) {
  185 + validate(entityId, new ValidationCallback(callback, ctx -> {
  186 + ResultSetFuture future = pluginCtx.tsService.findAllLatest(entityId);
181 187 Futures.addCallback(future, getCallback(callback, pluginCtx.tsService::convertResultSetToTsKvEntryList), executor);
182 188 }));
183 189 }
184 190
185 191 @Override
186   - public void loadLatestTimeseries(final DeviceId deviceId, final Collection<String> keys, final PluginCallback<List<TsKvEntry>> callback) {
187   - validate(deviceId, new ValidationCallback(callback, ctx -> {
188   - ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.findLatest(DataConstants.DEVICE, deviceId, keys);
  192 + public void loadLatestTimeseries(final EntityId entityId, final Collection<String> keys, final PluginCallback<List<TsKvEntry>> callback) {
  193 + validate(entityId, new ValidationCallback(callback, ctx -> {
  194 + ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.findLatest(entityId, keys);
189 195 Futures.addCallback(rsListFuture, getListCallback(callback, rsList ->
190 196 {
191 197 List<TsKvEntry> result = new ArrayList<>();
... ... @@ -268,24 +274,101 @@ public final class PluginProcessingContext implements PluginContext {
268 274 validate(deviceId, new ValidationCallback(callback, ctx -> callback.onSuccess(ctx, null)));
269 275 }
270 276
271   - private void validate(DeviceId deviceId, ValidationCallback callback) {
  277 + private void validate(EntityId entityId, ValidationCallback callback) {
272 278 if (securityCtx.isPresent()) {
273 279 final PluginApiCallSecurityContext ctx = securityCtx.get();
274   - if (ctx.isTenantAdmin() || ctx.isCustomerUser()) {
275   - ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(deviceId);
276   - Futures.addCallback(deviceFuture, getCallback(callback, device -> {
277   - if (device == null) {
278   - return Boolean.FALSE;
279   - } else {
280   - if (!device.getTenantId().equals(ctx.getTenantId())) {
281   - return Boolean.FALSE;
282   - } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) {
283   - return Boolean.FALSE;
  280 + if (ctx.isTenantAdmin() || ctx.isCustomerUser() || ctx.isSystemAdmin()) {
  281 + switch (entityId.getEntityType()) {
  282 + case DEVICE:
  283 + if (ctx.isSystemAdmin()) {
  284 + callback.onSuccess(this, Boolean.FALSE);
284 285 } else {
285   - return Boolean.TRUE;
  286 + ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
  287 + Futures.addCallback(deviceFuture, getCallback(callback, device -> {
  288 + if (device == null) {
  289 + return Boolean.FALSE;
  290 + } else {
  291 + if (!device.getTenantId().equals(ctx.getTenantId())) {
  292 + return Boolean.FALSE;
  293 + } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) {
  294 + return Boolean.FALSE;
  295 + } else {
  296 + return Boolean.TRUE;
  297 + }
  298 + }
  299 + }));
286 300 }
287   - }
288   - }));
  301 + return;
  302 + case ASSET:
  303 + if (ctx.isSystemAdmin()) {
  304 + callback.onSuccess(this, Boolean.FALSE);
  305 + } else {
  306 + ListenableFuture<Asset> assetFuture = pluginCtx.assetService.findAssetByIdAsync(new AssetId(entityId.getId()));
  307 + Futures.addCallback(assetFuture, getCallback(callback, asset -> {
  308 + if (asset == null) {
  309 + return Boolean.FALSE;
  310 + } else {
  311 + if (!asset.getTenantId().equals(ctx.getTenantId())) {
  312 + return Boolean.FALSE;
  313 + } else if (ctx.isCustomerUser() && !asset.getCustomerId().equals(ctx.getCustomerId())) {
  314 + return Boolean.FALSE;
  315 + } else {
  316 + return Boolean.TRUE;
  317 + }
  318 + }
  319 + }));
  320 + }
  321 + return;
  322 + case RULE:
  323 + if (ctx.isCustomerUser()) {
  324 + callback.onSuccess(this, Boolean.FALSE);
  325 + } else {
  326 + ListenableFuture<RuleMetaData> ruleFuture = pluginCtx.ruleService.findRuleByIdAsync(new RuleId(entityId.getId()));
  327 + Futures.addCallback(ruleFuture, getCallback(callback, rule -> rule != null && rule.getTenantId().equals(ctx.getTenantId())));
  328 + }
  329 + return;
  330 + case PLUGIN:
  331 + if (ctx.isCustomerUser()) {
  332 + callback.onSuccess(this, Boolean.FALSE);
  333 + } else {
  334 + ListenableFuture<PluginMetaData> pluginFuture = pluginCtx.pluginService.findPluginByIdAsync(new PluginId(entityId.getId()));
  335 + Futures.addCallback(pluginFuture, getCallback(callback, plugin -> plugin != null && plugin.getTenantId().equals(ctx.getTenantId())));
  336 + }
  337 + return;
  338 + case CUSTOMER:
  339 + if (ctx.isSystemAdmin()) {
  340 + callback.onSuccess(this, Boolean.FALSE);
  341 + } else {
  342 + ListenableFuture<Customer> customerFuture = pluginCtx.customerService.findCustomerByIdAsync(new CustomerId(entityId.getId()));
  343 + Futures.addCallback(customerFuture, getCallback(callback, customer -> {
  344 + if (customer == null) {
  345 + return Boolean.FALSE;
  346 + } else {
  347 + if (!customer.getTenantId().equals(ctx.getTenantId())) {
  348 + return Boolean.FALSE;
  349 + } else if (ctx.isCustomerUser() && !customer.getId().equals(ctx.getCustomerId())) {
  350 + return Boolean.FALSE;
  351 + } else {
  352 + return Boolean.TRUE;
  353 + }
  354 + }
  355 + }));
  356 + }
  357 + return;
  358 + case TENANT:
  359 + if (ctx.isCustomerUser()) {
  360 + callback.onSuccess(this, Boolean.FALSE);
  361 + } else if (ctx.isSystemAdmin()) {
  362 + callback.onSuccess(this, Boolean.TRUE);
  363 + } else {
  364 + ListenableFuture<Tenant> tenantFuture = pluginCtx.tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
  365 + Futures.addCallback(tenantFuture, getCallback(callback, tenant -> tenant != null && tenant.getId().equals(ctx.getTenantId())));
  366 + }
  367 + return;
  368 + default:
  369 + //TODO: add support of other entities
  370 + throw new IllegalStateException("Not Implemented!");
  371 + }
289 372 } else {
290 373 callback.onSuccess(this, Boolean.FALSE);
291 374 }
... ... @@ -295,8 +378,8 @@ public final class PluginProcessingContext implements PluginContext {
295 378 }
296 379
297 380 @Override
298   - public Optional<ServerAddress> resolve(DeviceId deviceId) {
299   - return pluginCtx.routingService.resolve(deviceId);
  381 + public Optional<ServerAddress> resolve(EntityId entityId) {
  382 + return pluginCtx.routingService.resolveById(entityId);
300 383 }
301 384
302 385 @Override
... ...
... ... @@ -25,8 +25,13 @@ import org.thingsboard.server.common.data.id.TenantId;
25 25 import org.thingsboard.server.common.msg.cluster.ServerAddress;
26 26 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
27 27 import org.thingsboard.server.common.data.id.PluginId;
  28 +import org.thingsboard.server.dao.asset.AssetService;
28 29 import org.thingsboard.server.dao.attributes.AttributesService;
  30 +import org.thingsboard.server.dao.customer.CustomerService;
29 31 import org.thingsboard.server.dao.device.DeviceService;
  32 +import org.thingsboard.server.dao.plugin.PluginService;
  33 +import org.thingsboard.server.dao.rule.RuleService;
  34 +import org.thingsboard.server.dao.tenant.TenantService;
30 35 import org.thingsboard.server.dao.timeseries.TimeseriesService;
31 36 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
32 37 import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
... ... @@ -46,7 +51,12 @@ public final class SharedPluginProcessingContext {
46 51 final ActorRef currentActor;
47 52 final ActorSystemContext systemContext;
48 53 final PluginWebSocketMsgEndpoint msgEndpoint;
  54 + final AssetService assetService;
49 55 final DeviceService deviceService;
  56 + final RuleService ruleService;
  57 + final PluginService pluginService;
  58 + final CustomerService customerService;
  59 + final TenantService tenantService;
50 60 final TimeseriesService tsService;
51 61 final AttributesService attributesService;
52 62 final ClusterRpcService rpcService;
... ... @@ -65,9 +75,14 @@ public final class SharedPluginProcessingContext {
65 75 this.msgEndpoint = sysContext.getWsMsgEndpoint();
66 76 this.tsService = sysContext.getTsService();
67 77 this.attributesService = sysContext.getAttributesService();
  78 + this.assetService = sysContext.getAssetService();
68 79 this.deviceService = sysContext.getDeviceService();
69 80 this.rpcService = sysContext.getRpcService();
70 81 this.routingService = sysContext.getRoutingService();
  82 + this.ruleService = sysContext.getRuleService();
  83 + this.pluginService = sysContext.getPluginService();
  84 + this.customerService = sysContext.getCustomerService();
  85 + this.tenantService = sysContext.getTenantService();
71 86 }
72 87
73 88 public PluginId getPluginId() {
... ... @@ -89,7 +104,7 @@ public final class SharedPluginProcessingContext {
89 104 }
90 105
91 106 private <T> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) {
92   - Optional<ServerAddress> instance = routingService.resolve(deviceId);
  107 + Optional<ServerAddress> instance = routingService.resolveById(deviceId);
93 108 if (instance.isPresent()) {
94 109 log.trace("[{}] Forwarding msg {} to remote device actor!", pluginId, msg);
95 110 rpcFunction.accept(instance.get(), msg);
... ...
... ... @@ -38,7 +38,7 @@ public class ValidationCallback implements PluginCallback<Boolean> {
38 38 if (value) {
39 39 action.accept(ctx);
40 40 } else {
41   - onFailure(ctx, new UnauthorizedException());
  41 + onFailure(ctx, new UnauthorizedException("Permission denied."));
42 42 }
43 43 }
44 44
... ...
... ... @@ -230,7 +230,7 @@ public class DefaultActorService implements ActorService {
230 230 @Override
231 231 public void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId) {
232 232 DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId);
233   - Optional<ServerAddress> address = actorContext.getRoutingService().resolve(deviceId);
  233 + Optional<ServerAddress> address = actorContext.getRoutingService().resolveById(deviceId);
234 234 if (address.isPresent()) {
235 235 rpcService.tell(address.get(), msg);
236 236 } else {
... ...
... ... @@ -116,7 +116,7 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
116 116 @Override
117 117 public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {
118 118 if (pendingMap.size() > 0 || subscribedToAttributeUpdates || subscribedToRpcCommands) {
119   - Optional<ServerAddress> newTargetServer = systemContext.getRoutingService().resolve(getDeviceId());
  119 + Optional<ServerAddress> newTargetServer = systemContext.getRoutingService().resolveById(getDeviceId());
120 120 if (!newTargetServer.equals(currentTargetServer)) {
121 121 firstMsg = true;
122 122 currentTargetServer = newTargetServer;
... ...
... ... @@ -81,13 +81,13 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
81 81 }
82 82
83 83 protected Optional<ServerAddress> forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward) {
84   - Optional<ServerAddress> address = systemContext.getRoutingService().resolve(toForward.getDeviceId());
  84 + Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
85 85 forwardToAppActor(ctx, toForward, address);
86 86 return address;
87 87 }
88 88
89 89 protected Optional<ServerAddress> forwardToAppActorIfAdressChanged(ActorContext ctx, ToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) {
90   - Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolve(toForward.getDeviceId());
  90 + Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
91 91 if (!newAddress.equals(oldAddress)) {
92 92 if (newAddress.isPresent()) {
93 93 systemContext.getRpcService().tell(newAddress.get(),
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.config;
  17 +
  18 +import org.springframework.boot.context.properties.ConfigurationProperties;
  19 +import org.springframework.context.annotation.Configuration;
  20 +import org.springframework.web.cors.CorsConfiguration;
  21 +
  22 +import java.util.HashMap;
  23 +import java.util.Map;
  24 +
  25 +/**
  26 + * Created by yyh on 2017/5/2.
  27 + * CORS configuration
  28 + */
  29 +@Configuration
  30 +@ConfigurationProperties(prefix = "spring.mvc.cors")
  31 +public class MvcCorsProperties {
  32 +
  33 + private Map<String, CorsConfiguration> mappings = new HashMap<>();
  34 +
  35 + public MvcCorsProperties() {
  36 + }
  37 +
  38 + public Map<String, CorsConfiguration> getMappings() {
  39 + return mappings;
  40 + }
  41 +
  42 + public void setMappings(Map<String, CorsConfiguration> mappings) {
  43 + this.mappings = mappings;
  44 + }
  45 +}
... ...
... ... @@ -18,7 +18,9 @@ package org.thingsboard.server.config;
18 18 import com.fasterxml.jackson.databind.ObjectMapper;
19 19 import org.springframework.beans.factory.annotation.Autowired;
20 20 import org.springframework.beans.factory.annotation.Qualifier;
  21 +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
21 22 import org.springframework.boot.autoconfigure.security.SecurityProperties;
  23 +import org.springframework.boot.context.properties.EnableConfigurationProperties;
22 24 import org.springframework.context.annotation.Bean;
23 25 import org.springframework.context.annotation.Configuration;
24 26 import org.springframework.core.annotation.Order;
... ... @@ -34,11 +36,15 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
34 36 import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
35 37 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
36 38 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
  39 +import org.springframework.web.cors.CorsUtils;
  40 +import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
  41 +import org.springframework.web.filter.CorsFilter;
37 42 import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
38 43 import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider;
39 44 import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter;
40 45 import org.thingsboard.server.service.security.auth.jwt.*;
41 46 import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor;
  47 +import org.thingsboard.server.service.security.auth.rest.RestPublicLoginProcessingFilter;
42 48
43 49 import java.util.ArrayList;
44 50 import java.util.Arrays;
... ... @@ -56,6 +62,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
56 62 public static final String WEBJARS_ENTRY_POINT = "/webjars/**";
57 63 public static final String DEVICE_API_ENTRY_POINT = "/api/v1/**";
58 64 public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
  65 + public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public";
59 66 public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
60 67 public static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/static/**", "/api/noauth/**", "/webjars/**"};
61 68 public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
... ... @@ -88,9 +95,17 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
88 95 }
89 96
90 97 @Bean
  98 + protected RestPublicLoginProcessingFilter buildRestPublicLoginProcessingFilter() throws Exception {
  99 + RestPublicLoginProcessingFilter filter = new RestPublicLoginProcessingFilter(PUBLIC_LOGIN_ENTRY_POINT, successHandler, failureHandler, objectMapper);
  100 + filter.setAuthenticationManager(this.authenticationManager);
  101 + return filter;
  102 + }
  103 +
  104 + @Bean
91 105 protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
92 106 List<String> pathsToSkip = new ArrayList(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS));
93   - pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT));
  107 + pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT,
  108 + PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT));
94 109 SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
95 110 JwtTokenAuthenticationProcessingFilter filter
96 111 = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher);
... ... @@ -136,6 +151,8 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
136 151 protected void configure(HttpSecurity http) throws Exception {
137 152 http.headers().cacheControl().disable().frameOptions().disable()
138 153 .and()
  154 + .cors()
  155 + .and()
139 156 .csrf().disable()
140 157 .exceptionHandling()
141 158 .and()
... ... @@ -146,6 +163,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
146 163 .antMatchers(WEBJARS_ENTRY_POINT).permitAll() // Webjars
147 164 .antMatchers(DEVICE_API_ENTRY_POINT).permitAll() // Device HTTP Transport API
148 165 .antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point
  166 + .antMatchers(PUBLIC_LOGIN_ENTRY_POINT).permitAll() // Public login end-point
149 167 .antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
150 168 .antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points
151 169 .and()
... ... @@ -156,8 +174,22 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
156 174 .exceptionHandling().accessDeniedHandler(restAccessDeniedHandler)
157 175 .and()
158 176 .addFilterBefore(buildRestLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
  177 + .addFilterBefore(buildRestPublicLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
159 178 .addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
160 179 .addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
161 180 .addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
162 181 }
  182 +
  183 +
  184 + @Bean
  185 + @ConditionalOnMissingBean(CorsFilter.class)
  186 + public CorsFilter corsFilter(@Autowired MvcCorsProperties mvcCorsProperties) {
  187 + if (mvcCorsProperties.getMappings().size() == 0) {
  188 + return new CorsFilter(new UrlBasedCorsConfigurationSource());
  189 + } else {
  190 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  191 + source.setCorsConfigurations(mvcCorsProperties.getMappings());
  192 + return new CorsFilter(source);
  193 + }
  194 + }
163 195 }
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.springframework.http.HttpStatus;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.web.bind.annotation.*;
  22 +import org.thingsboard.server.common.data.Customer;
  23 +import org.thingsboard.server.common.data.asset.Asset;
  24 +import org.thingsboard.server.common.data.id.AssetId;
  25 +import org.thingsboard.server.common.data.id.CustomerId;
  26 +import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.data.page.TextPageData;
  28 +import org.thingsboard.server.common.data.page.TextPageLink;
  29 +import org.thingsboard.server.dao.asset.AssetSearchQuery;
  30 +import org.thingsboard.server.dao.exception.IncorrectParameterException;
  31 +import org.thingsboard.server.dao.model.ModelConstants;
  32 +import org.thingsboard.server.exception.ThingsboardException;
  33 +import org.thingsboard.server.service.security.model.SecurityUser;
  34 +
  35 +import java.util.ArrayList;
  36 +import java.util.List;
  37 +import java.util.stream.Collectors;
  38 +
  39 +@RestController
  40 +@RequestMapping("/api")
  41 +public class AssetController extends BaseController {
  42 +
  43 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  44 + @RequestMapping(value = "/asset/{assetId}", method = RequestMethod.GET)
  45 + @ResponseBody
  46 + public Asset getAssetById(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
  47 + checkParameter("assetId", strAssetId);
  48 + try {
  49 + AssetId assetId = new AssetId(toUUID(strAssetId));
  50 + return checkAssetId(assetId);
  51 + } catch (Exception e) {
  52 + throw handleException(e);
  53 + }
  54 + }
  55 +
  56 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  57 + @RequestMapping(value = "/asset", method = RequestMethod.POST)
  58 + @ResponseBody
  59 + public Asset saveAsset(@RequestBody Asset asset) throws ThingsboardException {
  60 + try {
  61 + asset.setTenantId(getCurrentUser().getTenantId());
  62 + return checkNotNull(assetService.saveAsset(asset));
  63 + } catch (Exception e) {
  64 + throw handleException(e);
  65 + }
  66 + }
  67 +
  68 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  69 + @RequestMapping(value = "/asset/{assetId}", method = RequestMethod.DELETE)
  70 + @ResponseStatus(value = HttpStatus.OK)
  71 + public void deleteAsset(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
  72 + checkParameter("assetId", strAssetId);
  73 + try {
  74 + AssetId assetId = new AssetId(toUUID(strAssetId));
  75 + checkAssetId(assetId);
  76 + assetService.deleteAsset(assetId);
  77 + } catch (Exception e) {
  78 + throw handleException(e);
  79 + }
  80 + }
  81 +
  82 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  83 + @RequestMapping(value = "/customer/{customerId}/asset/{assetId}", method = RequestMethod.POST)
  84 + @ResponseBody
  85 + public Asset assignAssetToCustomer(@PathVariable("customerId") String strCustomerId,
  86 + @PathVariable("assetId") String strAssetId) throws ThingsboardException {
  87 + checkParameter("customerId", strCustomerId);
  88 + checkParameter("assetId", strAssetId);
  89 + try {
  90 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  91 + checkCustomerId(customerId);
  92 +
  93 + AssetId assetId = new AssetId(toUUID(strAssetId));
  94 + checkAssetId(assetId);
  95 +
  96 + return checkNotNull(assetService.assignAssetToCustomer(assetId, customerId));
  97 + } catch (Exception e) {
  98 + throw handleException(e);
  99 + }
  100 + }
  101 +
  102 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  103 + @RequestMapping(value = "/customer/asset/{assetId}", method = RequestMethod.DELETE)
  104 + @ResponseBody
  105 + public Asset unassignAssetFromCustomer(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
  106 + checkParameter("assetId", strAssetId);
  107 + try {
  108 + AssetId assetId = new AssetId(toUUID(strAssetId));
  109 + Asset asset = checkAssetId(assetId);
  110 + if (asset.getCustomerId() == null || asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
  111 + throw new IncorrectParameterException("Asset isn't assigned to any customer!");
  112 + }
  113 + return checkNotNull(assetService.unassignAssetFromCustomer(assetId));
  114 + } catch (Exception e) {
  115 + throw handleException(e);
  116 + }
  117 + }
  118 +
  119 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  120 + @RequestMapping(value = "/customer/public/asset/{assetId}", method = RequestMethod.POST)
  121 + @ResponseBody
  122 + public Asset assignAssetToPublicCustomer(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
  123 + checkParameter("assetId", strAssetId);
  124 + try {
  125 + AssetId assetId = new AssetId(toUUID(strAssetId));
  126 + Asset asset = checkAssetId(assetId);
  127 + Customer publicCustomer = customerService.findOrCreatePublicCustomer(asset.getTenantId());
  128 + return checkNotNull(assetService.assignAssetToCustomer(assetId, publicCustomer.getId()));
  129 + } catch (Exception e) {
  130 + throw handleException(e);
  131 + }
  132 + }
  133 +
  134 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  135 + @RequestMapping(value = "/tenant/assets", params = {"limit"}, method = RequestMethod.GET)
  136 + @ResponseBody
  137 + public TextPageData<Asset> getTenantAssets(
  138 + @RequestParam int limit,
  139 + @RequestParam(required = false) String textSearch,
  140 + @RequestParam(required = false) String idOffset,
  141 + @RequestParam(required = false) String textOffset) throws ThingsboardException {
  142 + try {
  143 + TenantId tenantId = getCurrentUser().getTenantId();
  144 + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
  145 + return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
  146 + } catch (Exception e) {
  147 + throw handleException(e);
  148 + }
  149 + }
  150 +
  151 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  152 + @RequestMapping(value = "/tenant/assets", params = {"assetName"}, method = RequestMethod.GET)
  153 + @ResponseBody
  154 + public Asset getTenantAsset(
  155 + @RequestParam String assetName) throws ThingsboardException {
  156 + try {
  157 + TenantId tenantId = getCurrentUser().getTenantId();
  158 + return checkNotNull(assetService.findAssetByTenantIdAndName(tenantId, assetName));
  159 + } catch (Exception e) {
  160 + throw handleException(e);
  161 + }
  162 + }
  163 +
  164 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  165 + @RequestMapping(value = "/customer/{customerId}/assets", params = {"limit"}, method = RequestMethod.GET)
  166 + @ResponseBody
  167 + public TextPageData<Asset> getCustomerAssets(
  168 + @PathVariable("customerId") String strCustomerId,
  169 + @RequestParam int limit,
  170 + @RequestParam(required = false) String textSearch,
  171 + @RequestParam(required = false) String idOffset,
  172 + @RequestParam(required = false) String textOffset) throws ThingsboardException {
  173 + checkParameter("customerId", strCustomerId);
  174 + try {
  175 + TenantId tenantId = getCurrentUser().getTenantId();
  176 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  177 + checkCustomerId(customerId);
  178 + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
  179 + return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
  180 + } catch (Exception e) {
  181 + throw handleException(e);
  182 + }
  183 + }
  184 +
  185 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  186 + @RequestMapping(value = "/assets", params = {"assetIds"}, method = RequestMethod.GET)
  187 + @ResponseBody
  188 + public List<Asset> getAssetsByIds(
  189 + @RequestParam("assetIds") String[] strAssetIds) throws ThingsboardException {
  190 + checkArrayParameter("assetIds", strAssetIds);
  191 + try {
  192 + SecurityUser user = getCurrentUser();
  193 + TenantId tenantId = user.getTenantId();
  194 + CustomerId customerId = user.getCustomerId();
  195 + List<AssetId> assetIds = new ArrayList<>();
  196 + for (String strAssetId : strAssetIds) {
  197 + assetIds.add(new AssetId(toUUID(strAssetId)));
  198 + }
  199 + ListenableFuture<List<Asset>> assets;
  200 + if (customerId == null || customerId.isNullUid()) {
  201 + assets = assetService.findAssetsByTenantIdAndIdsAsync(tenantId, assetIds);
  202 + } else {
  203 + assets = assetService.findAssetsByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, assetIds);
  204 + }
  205 + return checkNotNull(assets.get());
  206 + } catch (Exception e) {
  207 + throw handleException(e);
  208 + }
  209 + }
  210 +
  211 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  212 + @RequestMapping(value = "/assets", method = RequestMethod.POST)
  213 + @ResponseBody
  214 + public List<Asset> findByQuery(@RequestBody AssetSearchQuery query) throws ThingsboardException {
  215 + checkNotNull(query);
  216 + checkNotNull(query.getParameters());
  217 + checkNotNull(query.getAssetTypes());
  218 + checkEntityId(query.getParameters().getEntityId());
  219 + try {
  220 + List<Asset> assets = checkNotNull(assetService.findAssetsByQuery(query).get());
  221 + assets = assets.stream().filter(asset -> {
  222 + try {
  223 + checkAsset(asset);
  224 + return true;
  225 + } catch (ThingsboardException e) {
  226 + return false;
  227 + }
  228 + }).collect(Collectors.toList());
  229 + return assets;
  230 + } catch (Exception e) {
  231 + throw handleException(e);
  232 + }
  233 + }
  234 +}
... ...
... ... @@ -36,6 +36,7 @@ import org.thingsboard.server.exception.ThingsboardException;
36 36 import org.thingsboard.server.service.mail.MailService;
37 37 import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
38 38 import org.thingsboard.server.service.security.model.SecurityUser;
  39 +import org.thingsboard.server.service.security.model.UserPrincipal;
39 40 import org.thingsboard.server.service.security.model.token.JwtToken;
40 41 import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
41 42
... ... @@ -167,7 +168,8 @@ public class AuthController extends BaseController {
167 168 String encodedPassword = passwordEncoder.encode(password);
168 169 UserCredentials credentials = userService.activateUserCredentials(activateToken, encodedPassword);
169 170 User user = userService.findUserById(credentials.getUserId());
170   - SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled());
  171 + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
  172 + SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal);
171 173 String baseUrl = constructBaseUrl(request);
172 174 String loginUrl = String.format("%s/login", baseUrl);
173 175 String email = user.getEmail();
... ... @@ -201,7 +203,8 @@ public class AuthController extends BaseController {
201 203 userCredentials.setResetToken(null);
202 204 userCredentials = userService.saveUserCredentials(userCredentials);
203 205 User user = userService.findUserById(userCredentials.getUserId());
204   - SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled());
  206 + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
  207 + SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), principal);
205 208 String baseUrl = constructBaseUrl(request);
206 209 String loginUrl = String.format("%s/login", baseUrl);
207 210 String email = user.getEmail();
... ...
... ... @@ -22,10 +22,8 @@ import org.springframework.security.core.Authentication;
22 22 import org.springframework.security.core.context.SecurityContextHolder;
23 23 import org.springframework.web.bind.annotation.ExceptionHandler;
24 24 import org.thingsboard.server.actors.service.ActorService;
25   -import org.thingsboard.server.common.data.Customer;
26   -import org.thingsboard.server.common.data.Dashboard;
27   -import org.thingsboard.server.common.data.Device;
28   -import org.thingsboard.server.common.data.User;
  25 +import org.thingsboard.server.common.data.*;
  26 +import org.thingsboard.server.common.data.asset.Asset;
29 27 import org.thingsboard.server.common.data.id.*;
30 28 import org.thingsboard.server.common.data.page.TextPageLink;
31 29 import org.thingsboard.server.common.data.page.TimePageLink;
... ... @@ -36,6 +34,7 @@ import org.thingsboard.server.common.data.rule.RuleMetaData;
36 34 import org.thingsboard.server.common.data.security.Authority;
37 35 import org.thingsboard.server.common.data.widget.WidgetType;
38 36 import org.thingsboard.server.common.data.widget.WidgetsBundle;
  37 +import org.thingsboard.server.dao.asset.AssetService;
39 38 import org.thingsboard.server.dao.customer.CustomerService;
40 39 import org.thingsboard.server.dao.dashboard.DashboardService;
41 40 import org.thingsboard.server.dao.device.DeviceCredentialsService;
... ... @@ -44,6 +43,7 @@ import org.thingsboard.server.dao.exception.DataValidationException;
44 43 import org.thingsboard.server.dao.exception.IncorrectParameterException;
45 44 import org.thingsboard.server.dao.model.ModelConstants;
46 45 import org.thingsboard.server.dao.plugin.PluginService;
  46 +import org.thingsboard.server.dao.relation.RelationService;
47 47 import org.thingsboard.server.dao.rule.RuleService;
48 48 import org.thingsboard.server.dao.user.UserService;
49 49 import org.thingsboard.server.dao.widget.WidgetTypeService;
... ... @@ -79,6 +79,9 @@ public abstract class BaseController {
79 79 protected DeviceService deviceService;
80 80
81 81 @Autowired
  82 + protected AssetService assetService;
  83 +
  84 + @Autowired
82 85 protected DeviceCredentialsService deviceCredentialsService;
83 86
84 87 @Autowired
... ... @@ -102,6 +105,9 @@ public abstract class BaseController {
102 105 @Autowired
103 106 protected ActorService actorService;
104 107
  108 + @Autowired
  109 + protected RelationService relationService;
  110 +
105 111
106 112 @ExceptionHandler(ThingsboardException.class)
107 113 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
... ... @@ -251,6 +257,43 @@ public abstract class BaseController {
251 257 }
252 258 }
253 259
  260 + protected void checkEntityId(EntityId entityId) throws ThingsboardException {
  261 + try {
  262 + checkNotNull(entityId);
  263 + validateId(entityId.getId(), "Incorrect entityId " + entityId);
  264 + switch (entityId.getEntityType()) {
  265 + case DEVICE:
  266 + checkDevice(deviceService.findDeviceById(new DeviceId(entityId.getId())));
  267 + return;
  268 + case CUSTOMER:
  269 + checkCustomerId(new CustomerId(entityId.getId()));
  270 + return;
  271 + case TENANT:
  272 + checkTenantId(new TenantId(entityId.getId()));
  273 + return;
  274 + case PLUGIN:
  275 + checkPlugin(new PluginId(entityId.getId()));
  276 + return;
  277 + case RULE:
  278 + checkRule(new RuleId(entityId.getId()));
  279 + return;
  280 + case ASSET:
  281 + checkAsset(assetService.findAssetById(new AssetId(entityId.getId())));
  282 + return;
  283 + case DASHBOARD:
  284 + checkDashboardId(new DashboardId(entityId.getId()));
  285 + return;
  286 + case USER:
  287 + checkUserId(new UserId(entityId.getId()));
  288 + return;
  289 + default:
  290 + throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
  291 + }
  292 + } catch (Exception e) {
  293 + throw handleException(e, false);
  294 + }
  295 + }
  296 +
254 297 Device checkDeviceId(DeviceId deviceId) throws ThingsboardException {
255 298 try {
256 299 validateId(deviceId, "Incorrect deviceId " + deviceId);
... ... @@ -270,6 +313,25 @@ public abstract class BaseController {
270 313 }
271 314 }
272 315
  316 + Asset checkAssetId(AssetId assetId) throws ThingsboardException {
  317 + try {
  318 + validateId(assetId, "Incorrect assetId " + assetId);
  319 + Asset asset = assetService.findAssetById(assetId);
  320 + checkAsset(asset);
  321 + return asset;
  322 + } catch (Exception e) {
  323 + throw handleException(e, false);
  324 + }
  325 + }
  326 +
  327 + protected void checkAsset(Asset asset) throws ThingsboardException {
  328 + checkNotNull(asset);
  329 + checkTenantId(asset.getTenantId());
  330 + if (asset.getCustomerId() != null && !asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
  331 + checkCustomerId(asset.getCustomerId());
  332 + }
  333 + }
  334 +
273 335 WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException {
274 336 try {
275 337 validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId);
... ... @@ -385,6 +447,16 @@ public abstract class BaseController {
385 447 return plugin;
386 448 }
387 449
  450 + protected PluginMetaData checkPlugin(PluginId pluginId) throws ThingsboardException {
  451 + checkNotNull(pluginId);
  452 + return checkPlugin(pluginService.findPluginById(pluginId));
  453 + }
  454 +
  455 + protected RuleMetaData checkRule(RuleId ruleId) throws ThingsboardException {
  456 + checkNotNull(ruleId);
  457 + return checkRule(ruleService.findRuleById(ruleId));
  458 + }
  459 +
388 460 protected RuleMetaData checkRule(RuleMetaData rule) throws ThingsboardException {
389 461 checkNotNull(rule);
390 462 SecurityUser authUser = getCurrentUser();
... ... @@ -410,7 +482,8 @@ public abstract class BaseController {
410 482 if (request.getHeader("x-forwarded-port") != null) {
411 483 try {
412 484 serverPort = request.getIntHeader("x-forwarded-port");
413   - } catch (NumberFormatException e) {}
  485 + } catch (NumberFormatException e) {
  486 + }
414 487 }
415 488
416 489 String baseUrl = String.format("%s://%s:%d",
... ...
... ... @@ -15,6 +15,9 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
  20 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 21 import org.springframework.http.HttpStatus;
19 22 import org.springframework.security.access.prepost.PreAuthorize;
20 23 import org.springframework.web.bind.annotation.*;
... ... @@ -43,6 +46,28 @@ public class CustomerController extends BaseController {
43 46 }
44 47
45 48 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  49 + @RequestMapping(value = "/customer/{customerId}/shortInfo", method = RequestMethod.GET)
  50 + @ResponseBody
  51 + public JsonNode getShortCustomerInfoById(@PathVariable("customerId") String strCustomerId) throws ThingsboardException {
  52 + checkParameter("customerId", strCustomerId);
  53 + try {
  54 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  55 + Customer customer = checkCustomerId(customerId);
  56 + ObjectMapper objectMapper = new ObjectMapper();
  57 + ObjectNode infoObject = objectMapper.createObjectNode();
  58 + infoObject.put("title", customer.getTitle());
  59 + boolean isPublic = false;
  60 + if (customer.getAdditionalInfo() != null && customer.getAdditionalInfo().has("isPublic")) {
  61 + isPublic = customer.getAdditionalInfo().get("isPublic").asBoolean();
  62 + }
  63 + infoObject.put("isPublic", isPublic);
  64 + return infoObject;
  65 + } catch (Exception e) {
  66 + throw handleException(e);
  67 + }
  68 + }
  69 +
  70 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
46 71 @RequestMapping(value = "/customer/{customerId}/title", method = RequestMethod.GET, produces = "application/text")
47 72 @ResponseBody
48 73 public String getCustomerTitleById(@PathVariable("customerId") String strCustomerId) throws ThingsboardException {
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.controller;
18 18 import org.springframework.http.HttpStatus;
19 19 import org.springframework.security.access.prepost.PreAuthorize;
20 20 import org.springframework.web.bind.annotation.*;
  21 +import org.thingsboard.server.common.data.Customer;
21 22 import org.thingsboard.server.common.data.Dashboard;
22 23 import org.thingsboard.server.common.data.DashboardInfo;
23 24 import org.thingsboard.server.common.data.id.CustomerId;
... ... @@ -117,6 +118,21 @@ public class DashboardController extends BaseController {
117 118 }
118 119
119 120 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  121 + @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST)
  122 + @ResponseBody
  123 + public Dashboard assignDashboardToPublicCustomer(@PathVariable("dashboardId") String strDashboardId) throws ThingsboardException {
  124 + checkParameter("dashboardId", strDashboardId);
  125 + try {
  126 + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
  127 + Dashboard dashboard = checkDashboardId(dashboardId);
  128 + Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
  129 + return checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, publicCustomer.getId()));
  130 + } catch (Exception e) {
  131 + throw handleException(e);
  132 + }
  133 + }
  134 +
  135 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
120 136 @RequestMapping(value = "/tenant/dashboards", params = { "limit" }, method = RequestMethod.GET)
121 137 @ResponseBody
122 138 public TextPageData<DashboardInfo> getTenantDashboards(
... ...
... ... @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
19 19 import org.springframework.http.HttpStatus;
20 20 import org.springframework.security.access.prepost.PreAuthorize;
21 21 import org.springframework.web.bind.annotation.*;
  22 +import org.thingsboard.server.common.data.Customer;
22 23 import org.thingsboard.server.common.data.Device;
23 24 import org.thingsboard.server.common.data.id.CustomerId;
24 25 import org.thingsboard.server.common.data.id.DeviceId;
... ... @@ -114,6 +115,21 @@ public class DeviceController extends BaseController {
114 115 }
115 116 }
116 117
  118 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  119 + @RequestMapping(value = "/customer/public/device/{deviceId}", method = RequestMethod.POST)
  120 + @ResponseBody
  121 + public Device assignDeviceToPublicCustomer(@PathVariable("deviceId") String strDeviceId) throws ThingsboardException {
  122 + checkParameter("deviceId", strDeviceId);
  123 + try {
  124 + DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
  125 + Device device = checkDeviceId(deviceId);
  126 + Customer publicCustomer = customerService.findOrCreatePublicCustomer(device.getTenantId());
  127 + return checkNotNull(deviceService.assignDeviceToCustomer(deviceId, publicCustomer.getId()));
  128 + } catch (Exception e) {
  129 + throw handleException(e);
  130 + }
  131 + }
  132 +
117 133 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
118 134 @RequestMapping(value = "/device/{deviceId}/credentials", method = RequestMethod.GET)
119 135 @ResponseBody
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import org.springframework.http.HttpStatus;
  19 +import org.springframework.security.access.prepost.PreAuthorize;
  20 +import org.springframework.web.bind.annotation.*;
  21 +import org.thingsboard.server.common.data.id.EntityId;
  22 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  23 +import org.thingsboard.server.common.data.relation.EntityRelation;
  24 +import org.thingsboard.server.dao.relation.EntityRelationsQuery;
  25 +import org.thingsboard.server.exception.ThingsboardErrorCode;
  26 +import org.thingsboard.server.exception.ThingsboardException;
  27 +
  28 +import java.util.List;
  29 +
  30 +
  31 +@RestController
  32 +@RequestMapping("/api")
  33 +public class EntityRelationController extends BaseController {
  34 +
  35 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  36 + @RequestMapping(value = "/relation", method = RequestMethod.POST)
  37 + @ResponseStatus(value = HttpStatus.OK)
  38 + public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException {
  39 + try {
  40 + checkNotNull(relation);
  41 + checkEntityId(relation.getFrom());
  42 + checkEntityId(relation.getTo());
  43 + relationService.saveRelation(relation).get();
  44 + } catch (Exception e) {
  45 + throw handleException(e);
  46 + }
  47 + }
  48 +
  49 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  50 + @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {"fromId", "fromType", "relationType", "toId", "toType"})
  51 + @ResponseStatus(value = HttpStatus.OK)
  52 + public void deleteRelation(@RequestParam("fromId") String strFromId,
  53 + @RequestParam("fromType") String strFromType, @RequestParam("relationType") String strRelationType,
  54 + @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException {
  55 + checkParameter("fromId", strFromId);
  56 + checkParameter("fromType", strFromType);
  57 + checkParameter("relationType", strRelationType);
  58 + checkParameter("toId", strToId);
  59 + checkParameter("toType", strToType);
  60 + EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
  61 + EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
  62 + checkEntityId(fromId);
  63 + checkEntityId(toId);
  64 + try {
  65 + Boolean found = relationService.deleteRelation(fromId, toId, strRelationType).get();
  66 + if (!found) {
  67 + throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
  68 + }
  69 + } catch (Exception e) {
  70 + throw handleException(e);
  71 + }
  72 + }
  73 +
  74 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  75 + @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"id", "type"})
  76 + @ResponseStatus(value = HttpStatus.OK)
  77 + public void deleteRelations(@RequestParam("entityId") String strId,
  78 + @RequestParam("entityType") String strType) throws ThingsboardException {
  79 + checkParameter("entityId", strId);
  80 + checkParameter("entityType", strType);
  81 + EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId);
  82 + checkEntityId(entityId);
  83 + try {
  84 + relationService.deleteEntityRelations(entityId).get();
  85 + } catch (Exception e) {
  86 + throw handleException(e);
  87 + }
  88 + }
  89 +
  90 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  91 + @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType", "toId", "toType"})
  92 + @ResponseStatus(value = HttpStatus.OK)
  93 + public void checkRelation(@RequestParam("fromId") String strFromId,
  94 + @RequestParam("fromType") String strFromType, @RequestParam("relationType") String strRelationType,
  95 + @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException {
  96 + try {
  97 + checkParameter("fromId", strFromId);
  98 + checkParameter("fromType", strFromType);
  99 + checkParameter("relationType", strRelationType);
  100 + checkParameter("toId", strToId);
  101 + checkParameter("toType", strToType);
  102 + EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
  103 + EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
  104 + checkEntityId(fromId);
  105 + checkEntityId(toId);
  106 + Boolean found = relationService.checkRelation(fromId, toId, strRelationType).get();
  107 + if (!found) {
  108 + throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
  109 + }
  110 + } catch (Exception e) {
  111 + throw handleException(e);
  112 + }
  113 + }
  114 +
  115 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  116 + @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType"})
  117 + @ResponseBody
  118 + public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType) throws ThingsboardException {
  119 + checkParameter("fromId", strFromId);
  120 + checkParameter("fromType", strFromType);
  121 + EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
  122 + checkEntityId(entityId);
  123 + try {
  124 + return checkNotNull(relationService.findByFrom(entityId).get());
  125 + } catch (Exception e) {
  126 + throw handleException(e);
  127 + }
  128 + }
  129 +
  130 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  131 + @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"})
  132 + @ResponseBody
  133 + public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType
  134 + , @RequestParam("relationType") String strRelationType) throws ThingsboardException {
  135 + checkParameter("fromId", strFromId);
  136 + checkParameter("fromType", strFromType);
  137 + checkParameter("relationType", strRelationType);
  138 + EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
  139 + checkEntityId(entityId);
  140 + try {
  141 + return checkNotNull(relationService.findByFromAndType(entityId, strRelationType).get());
  142 + } catch (Exception e) {
  143 + throw handleException(e);
  144 + }
  145 + }
  146 +
  147 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  148 + @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType"})
  149 + @ResponseBody
  150 + public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException {
  151 + checkParameter("toId", strToId);
  152 + checkParameter("toType", strToType);
  153 + EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
  154 + checkEntityId(entityId);
  155 + try {
  156 + return checkNotNull(relationService.findByTo(entityId).get());
  157 + } catch (Exception e) {
  158 + throw handleException(e);
  159 + }
  160 + }
  161 +
  162 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  163 + @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType", "relationType"})
  164 + @ResponseBody
  165 + public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, @RequestParam("toType") String strToType
  166 + , @RequestParam("relationType") String strRelationType) throws ThingsboardException {
  167 + checkParameter("toId", strToId);
  168 + checkParameter("toType", strToType);
  169 + checkParameter("relationType", strRelationType);
  170 + EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
  171 + checkEntityId(entityId);
  172 + try {
  173 + return checkNotNull(relationService.findByToAndType(entityId, strRelationType).get());
  174 + } catch (Exception e) {
  175 + throw handleException(e);
  176 + }
  177 + }
  178 +
  179 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  180 + @RequestMapping(value = "/relations", method = RequestMethod.POST)
  181 + @ResponseBody
  182 + public List<EntityRelation> findByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException {
  183 + checkNotNull(query);
  184 + checkNotNull(query.getParameters());
  185 + checkNotNull(query.getFilters());
  186 + checkEntityId(query.getParameters().getEntityId());
  187 + try {
  188 + return checkNotNull(relationService.findByQuery(query).get());
  189 + } catch (Exception e) {
  190 + throw handleException(e);
  191 + }
  192 + }
  193 +
  194 +}
... ...
... ... @@ -18,7 +18,6 @@ package org.thingsboard.server.controller;
18 18 import org.springframework.beans.factory.annotation.Autowired;
19 19 import org.springframework.security.access.prepost.PreAuthorize;
20 20 import org.springframework.web.bind.annotation.*;
21   -import org.thingsboard.server.common.data.EntityType;
22 21 import org.thingsboard.server.common.data.Event;
23 22 import org.thingsboard.server.common.data.id.*;
24 23 import org.thingsboard.server.common.data.page.TimePageData;
... ... @@ -59,7 +58,7 @@ public class EventController extends BaseController {
59 58 ThingsboardErrorCode.PERMISSION_DENIED);
60 59 }
61 60 TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
62   - return checkNotNull(eventService.findEvents(tenantId, getEntityId(strEntityType, strEntityId), eventType, pageLink));
  61 + return checkNotNull(eventService.findEvents(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), eventType, pageLink));
63 62 } catch (Exception e) {
64 63 throw handleException(e);
65 64 }
... ... @@ -88,29 +87,10 @@ public class EventController extends BaseController {
88 87 ThingsboardErrorCode.PERMISSION_DENIED);
89 88 }
90 89 TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
91   - return checkNotNull(eventService.findEvents(tenantId, getEntityId(strEntityType, strEntityId), pageLink));
  90 + return checkNotNull(eventService.findEvents(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), pageLink));
92 91 } catch (Exception e) {
93 92 throw handleException(e);
94 93 }
95 94 }
96 95
97   -
98   - private EntityId getEntityId(String strEntityType, String strEntityId) throws ThingsboardException {
99   - EntityId entityId;
100   - EntityType entityType = EntityType.valueOf(strEntityType);
101   - switch (entityType) {
102   - case RULE:
103   - entityId = new RuleId(toUUID(strEntityId));
104   - break;
105   - case PLUGIN:
106   - entityId = new PluginId(toUUID(strEntityId));
107   - break;
108   - case DEVICE:
109   - entityId = new DeviceId(toUUID(strEntityId));
110   - break;
111   - default:
112   - throw new ThingsboardException("EntityType ['" + entityType + "'] is incorrect!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
113   - }
114   - return entityId;
115   - }
116 96 }
... ...
... ... @@ -15,11 +15,13 @@
15 15 */
16 16 package org.thingsboard.server.service.cluster.routing;
17 17
  18 +import org.thingsboard.server.common.data.id.EntityId;
18 19 import org.thingsboard.server.common.data.id.UUIDBased;
19 20 import org.thingsboard.server.common.msg.cluster.ServerAddress;
20 21 import org.thingsboard.server.service.cluster.discovery.ServerInstance;
21 22
22 23 import java.util.Optional;
  24 +import java.util.UUID;
23 25
24 26 /**
25 27 * @author Andrew Shvayka
... ... @@ -28,6 +30,8 @@ public interface ClusterRoutingService {
28 30
29 31 ServerAddress getCurrentServer();
30 32
31   - Optional<ServerAddress> resolve(UUIDBased entityId);
  33 + Optional<ServerAddress> resolveByUuid(UUID uuid);
  34 +
  35 + Optional<ServerAddress> resolveById(EntityId entityId);
32 36
33 37 }
... ...
... ... @@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
22 22 import org.springframework.beans.factory.annotation.Value;
23 23 import org.springframework.stereotype.Service;
24 24 import org.springframework.util.Assert;
  25 +import org.thingsboard.server.common.data.id.EntityId;
25 26 import org.thingsboard.server.common.data.id.UUIDBased;
26 27 import org.thingsboard.server.common.msg.cluster.ServerAddress;
27 28 import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
... ... @@ -31,6 +32,7 @@ import org.thingsboard.server.utils.MiscUtils;
31 32
32 33 import javax.annotation.PostConstruct;
33 34 import java.util.Optional;
  35 +import java.util.UUID;
34 36 import java.util.concurrent.ConcurrentNavigableMap;
35 37 import java.util.concurrent.ConcurrentSkipListMap;
36 38
... ... @@ -77,13 +79,18 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D
77 79 }
78 80
79 81 @Override
80   - public Optional<ServerAddress> resolve(UUIDBased entityId) {
81   - Assert.notNull(entityId);
  82 + public Optional<ServerAddress> resolveById(EntityId entityId) {
  83 + return resolveByUuid(entityId.getId());
  84 + }
  85 +
  86 + @Override
  87 + public Optional<ServerAddress> resolveByUuid(UUID uuid) {
  88 + Assert.notNull(uuid);
82 89 if (circle.isEmpty()) {
83 90 return Optional.empty();
84 91 }
85   - Long hash = hashFunction.newHasher().putLong(entityId.getId().getMostSignificantBits())
86   - .putLong(entityId.getId().getLeastSignificantBits()).hash().asLong();
  92 + Long hash = hashFunction.newHasher().putLong(uuid.getMostSignificantBits())
  93 + .putLong(uuid.getLeastSignificantBits()).hash().asLong();
87 94 if (!circle.containsKey(hash)) {
88 95 ConcurrentNavigableMap<Long, ServerInstance> tailMap =
89 96 circle.tailMap(hash);
... ...
... ... @@ -16,32 +16,40 @@
16 16 package org.thingsboard.server.service.security.auth.jwt;
17 17
18 18 import org.springframework.beans.factory.annotation.Autowired;
19   -import org.springframework.security.authentication.AuthenticationProvider;
20   -import org.springframework.security.authentication.DisabledException;
21   -import org.springframework.security.authentication.InsufficientAuthenticationException;
22   -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  19 +import org.springframework.security.authentication.*;
23 20 import org.springframework.security.core.Authentication;
24 21 import org.springframework.security.core.AuthenticationException;
25 22 import org.springframework.security.core.userdetails.UsernameNotFoundException;
26 23 import org.springframework.stereotype.Component;
27 24 import org.springframework.util.Assert;
  25 +import org.thingsboard.server.common.data.Customer;
28 26 import org.thingsboard.server.common.data.User;
  27 +import org.thingsboard.server.common.data.id.CustomerId;
  28 +import org.thingsboard.server.common.data.id.UUIDBased;
  29 +import org.thingsboard.server.common.data.id.UserId;
  30 +import org.thingsboard.server.common.data.security.Authority;
29 31 import org.thingsboard.server.common.data.security.UserCredentials;
  32 +import org.thingsboard.server.dao.customer.CustomerService;
30 33 import org.thingsboard.server.dao.user.UserService;
31 34 import org.thingsboard.server.service.security.auth.RefreshAuthenticationToken;
32 35 import org.thingsboard.server.service.security.model.SecurityUser;
  36 +import org.thingsboard.server.service.security.model.UserPrincipal;
33 37 import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
34 38 import org.thingsboard.server.service.security.model.token.RawAccessJwtToken;
35 39
  40 +import java.util.UUID;
  41 +
36 42 @Component
37 43 public class RefreshTokenAuthenticationProvider implements AuthenticationProvider {
38 44
39 45 private final JwtTokenFactory tokenFactory;
40 46 private final UserService userService;
  47 + private final CustomerService customerService;
41 48
42 49 @Autowired
43   - public RefreshTokenAuthenticationProvider(final UserService userService, final JwtTokenFactory tokenFactory) {
  50 + public RefreshTokenAuthenticationProvider(final UserService userService, final CustomerService customerService, final JwtTokenFactory tokenFactory) {
44 51 this.userService = userService;
  52 + this.customerService = customerService;
45 53 this.tokenFactory = tokenFactory;
46 54 }
47 55
... ... @@ -50,8 +58,18 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide
50 58 Assert.notNull(authentication, "No authentication data provided");
51 59 RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials();
52 60 SecurityUser unsafeUser = tokenFactory.parseRefreshToken(rawAccessToken);
  61 + UserPrincipal principal = unsafeUser.getUserPrincipal();
  62 + SecurityUser securityUser;
  63 + if (principal.getType() == UserPrincipal.Type.USER_NAME) {
  64 + securityUser = authenticateByUserId(unsafeUser.getId());
  65 + } else {
  66 + securityUser = authenticateByPublicId(principal.getValue());
  67 + }
  68 + return new RefreshAuthenticationToken(securityUser);
  69 + }
53 70
54   - User user = userService.findUserById(unsafeUser.getId());
  71 + private SecurityUser authenticateByUserId(UserId userId) {
  72 + User user = userService.findUserById(userId);
55 73 if (user == null) {
56 74 throw new UsernameNotFoundException("User not found by refresh token");
57 75 }
... ... @@ -67,9 +85,44 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide
67 85
68 86 if (user.getAuthority() == null) throw new InsufficientAuthenticationException("User has no authority assigned");
69 87
70   - SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled());
  88 + UserPrincipal userPrincipal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
71 89
72   - return new RefreshAuthenticationToken(securityUser);
  90 + SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), userPrincipal);
  91 +
  92 + return securityUser;
  93 + }
  94 +
  95 + private SecurityUser authenticateByPublicId(String publicId) {
  96 + CustomerId customerId;
  97 + try {
  98 + customerId = new CustomerId(UUID.fromString(publicId));
  99 + } catch (Exception e) {
  100 + throw new BadCredentialsException("Refresh token is not valid");
  101 + }
  102 + Customer publicCustomer = customerService.findCustomerById(customerId);
  103 + if (publicCustomer == null) {
  104 + throw new UsernameNotFoundException("Public entity not found by refresh token");
  105 + }
  106 + boolean isPublic = false;
  107 + if (publicCustomer.getAdditionalInfo() != null && publicCustomer.getAdditionalInfo().has("isPublic")) {
  108 + isPublic = publicCustomer.getAdditionalInfo().get("isPublic").asBoolean();
  109 + }
  110 + if (!isPublic) {
  111 + throw new BadCredentialsException("Refresh token is not valid");
  112 + }
  113 + User user = new User(new UserId(UUIDBased.EMPTY));
  114 + user.setTenantId(publicCustomer.getTenantId());
  115 + user.setCustomerId(publicCustomer.getId());
  116 + user.setEmail(publicId);
  117 + user.setAuthority(Authority.CUSTOMER_USER);
  118 + user.setFirstName("Public");
  119 + user.setLastName("Public");
  120 +
  121 + UserPrincipal userPrincipal = new UserPrincipal(UserPrincipal.Type.PUBLIC_ID, publicId);
  122 +
  123 + SecurityUser securityUser = new SecurityUser(user, true, userPrincipal);
  124 +
  125 + return securityUser;
73 126 }
74 127
75 128 @Override
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.security.auth.rest;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonProperty;
  20 +
  21 +public class PublicLoginRequest {
  22 +
  23 + private String publicId;
  24 +
  25 + @JsonCreator
  26 + public PublicLoginRequest(@JsonProperty("publicId") String publicId) {
  27 + this.publicId = publicId;
  28 + }
  29 +
  30 + public String getPublicId() {
  31 + return publicId;
  32 + }
  33 +
  34 +}
... ...
... ... @@ -23,20 +23,31 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
23 23 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
24 24 import org.springframework.stereotype.Component;
25 25 import org.springframework.util.Assert;
  26 +import org.thingsboard.server.common.data.Customer;
26 27 import org.thingsboard.server.common.data.User;
  28 +import org.thingsboard.server.common.data.id.CustomerId;
  29 +import org.thingsboard.server.common.data.id.UUIDBased;
  30 +import org.thingsboard.server.common.data.id.UserId;
  31 +import org.thingsboard.server.common.data.security.Authority;
27 32 import org.thingsboard.server.common.data.security.UserCredentials;
  33 +import org.thingsboard.server.dao.customer.CustomerService;
28 34 import org.thingsboard.server.dao.user.UserService;
29 35 import org.thingsboard.server.service.security.model.SecurityUser;
  36 +import org.thingsboard.server.service.security.model.UserPrincipal;
  37 +
  38 +import java.util.UUID;
30 39
31 40 @Component
32 41 public class RestAuthenticationProvider implements AuthenticationProvider {
33 42
34 43 private final BCryptPasswordEncoder encoder;
35 44 private final UserService userService;
  45 + private final CustomerService customerService;
36 46
37 47 @Autowired
38   - public RestAuthenticationProvider(final UserService userService, final BCryptPasswordEncoder encoder) {
  48 + public RestAuthenticationProvider(final UserService userService, final CustomerService customerService, final BCryptPasswordEncoder encoder) {
39 49 this.userService = userService;
  50 + this.customerService = customerService;
40 51 this.encoder = encoder;
41 52 }
42 53
... ... @@ -44,9 +55,23 @@ public class RestAuthenticationProvider implements AuthenticationProvider {
44 55 public Authentication authenticate(Authentication authentication) throws AuthenticationException {
45 56 Assert.notNull(authentication, "No authentication data provided");
46 57
47   - String username = (String) authentication.getPrincipal();
48   - String password = (String) authentication.getCredentials();
  58 + Object principal = authentication.getPrincipal();
  59 + if (!(principal instanceof UserPrincipal)) {
  60 + throw new BadCredentialsException("Authentication Failed. Bad user principal.");
  61 + }
49 62
  63 + UserPrincipal userPrincipal = (UserPrincipal) principal;
  64 + if (userPrincipal.getType() == UserPrincipal.Type.USER_NAME) {
  65 + String username = userPrincipal.getValue();
  66 + String password = (String) authentication.getCredentials();
  67 + return authenticateByUsernameAndPassword(userPrincipal, username, password);
  68 + } else {
  69 + String publicId = userPrincipal.getValue();
  70 + return authenticateByPublicId(userPrincipal, publicId);
  71 + }
  72 + }
  73 +
  74 + private Authentication authenticateByUsernameAndPassword(UserPrincipal userPrincipal, String username, String password) {
50 75 User user = userService.findUserByEmail(username);
51 76 if (user == null) {
52 77 throw new UsernameNotFoundException("User not found: " + username);
... ... @@ -67,7 +92,38 @@ public class RestAuthenticationProvider implements AuthenticationProvider {
67 92
68 93 if (user.getAuthority() == null) throw new InsufficientAuthenticationException("User has no authority assigned");
69 94
70   - SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled());
  95 + SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), userPrincipal);
  96 +
  97 + return new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
  98 + }
  99 +
  100 + private Authentication authenticateByPublicId(UserPrincipal userPrincipal, String publicId) {
  101 + CustomerId customerId;
  102 + try {
  103 + customerId = new CustomerId(UUID.fromString(publicId));
  104 + } catch (Exception e) {
  105 + throw new BadCredentialsException("Authentication Failed. Public Id is not valid.");
  106 + }
  107 + Customer publicCustomer = customerService.findCustomerById(customerId);
  108 + if (publicCustomer == null) {
  109 + throw new UsernameNotFoundException("Public entity not found: " + publicId);
  110 + }
  111 + boolean isPublic = false;
  112 + if (publicCustomer.getAdditionalInfo() != null && publicCustomer.getAdditionalInfo().has("isPublic")) {
  113 + isPublic = publicCustomer.getAdditionalInfo().get("isPublic").asBoolean();
  114 + }
  115 + if (!isPublic) {
  116 + throw new BadCredentialsException("Authentication Failed. Public Id is not valid.");
  117 + }
  118 + User user = new User(new UserId(UUIDBased.EMPTY));
  119 + user.setTenantId(publicCustomer.getTenantId());
  120 + user.setCustomerId(publicCustomer.getId());
  121 + user.setEmail(publicId);
  122 + user.setAuthority(Authority.CUSTOMER_USER);
  123 + user.setFirstName("Public");
  124 + user.setLastName("Public");
  125 +
  126 + SecurityUser securityUser = new SecurityUser(user, true, userPrincipal);
71 127
72 128 return new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
73 129 }
... ...
... ... @@ -29,6 +29,7 @@ import org.springframework.security.web.authentication.AbstractAuthenticationPro
29 29 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
30 30 import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
31 31 import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException;
  32 +import org.thingsboard.server.service.security.model.UserPrincipal;
32 33
33 34 import javax.servlet.FilterChain;
34 35 import javax.servlet.ServletException;
... ... @@ -73,7 +74,9 @@ public class RestLoginProcessingFilter extends AbstractAuthenticationProcessingF
73 74 throw new AuthenticationServiceException("Username or Password not provided");
74 75 }
75 76
76   - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
  77 + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, loginRequest.getUsername());
  78 +
  79 + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(principal, loginRequest.getPassword());
77 80
78 81 return this.getAuthenticationManager().authenticate(token);
79 82 }
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.security.auth.rest;
  17 +
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
  19 +import org.apache.commons.lang3.StringUtils;
  20 +import org.slf4j.Logger;
  21 +import org.slf4j.LoggerFactory;
  22 +import org.springframework.http.HttpMethod;
  23 +import org.springframework.security.authentication.AuthenticationServiceException;
  24 +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  25 +import org.springframework.security.core.Authentication;
  26 +import org.springframework.security.core.AuthenticationException;
  27 +import org.springframework.security.core.context.SecurityContextHolder;
  28 +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
  29 +import org.springframework.security.web.authentication.AuthenticationFailureHandler;
  30 +import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
  31 +import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException;
  32 +import org.thingsboard.server.service.security.model.UserPrincipal;
  33 +
  34 +import javax.servlet.FilterChain;
  35 +import javax.servlet.ServletException;
  36 +import javax.servlet.http.HttpServletRequest;
  37 +import javax.servlet.http.HttpServletResponse;
  38 +import java.io.IOException;
  39 +
  40 +public class RestPublicLoginProcessingFilter extends AbstractAuthenticationProcessingFilter {
  41 + private static Logger logger = LoggerFactory.getLogger(RestPublicLoginProcessingFilter.class);
  42 +
  43 + private final AuthenticationSuccessHandler successHandler;
  44 + private final AuthenticationFailureHandler failureHandler;
  45 +
  46 + private final ObjectMapper objectMapper;
  47 +
  48 + public RestPublicLoginProcessingFilter(String defaultProcessUrl, AuthenticationSuccessHandler successHandler,
  49 + AuthenticationFailureHandler failureHandler, ObjectMapper mapper) {
  50 + super(defaultProcessUrl);
  51 + this.successHandler = successHandler;
  52 + this.failureHandler = failureHandler;
  53 + this.objectMapper = mapper;
  54 + }
  55 +
  56 + @Override
  57 + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
  58 + throws AuthenticationException, IOException, ServletException {
  59 + if (!HttpMethod.POST.name().equals(request.getMethod())) {
  60 + if(logger.isDebugEnabled()) {
  61 + logger.debug("Authentication method not supported. Request method: " + request.getMethod());
  62 + }
  63 + throw new AuthMethodNotSupportedException("Authentication method not supported");
  64 + }
  65 +
  66 + PublicLoginRequest loginRequest;
  67 + try {
  68 + loginRequest = objectMapper.readValue(request.getReader(), PublicLoginRequest.class);
  69 + } catch (Exception e) {
  70 + throw new AuthenticationServiceException("Invalid public login request payload");
  71 + }
  72 +
  73 + if (StringUtils.isBlank(loginRequest.getPublicId())) {
  74 + throw new AuthenticationServiceException("Public Id is not provided");
  75 + }
  76 +
  77 + UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.PUBLIC_ID, loginRequest.getPublicId());
  78 +
  79 + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(principal, "");
  80 +
  81 + return this.getAuthenticationManager().authenticate(token);
  82 + }
  83 +
  84 + @Override
  85 + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
  86 + Authentication authResult) throws IOException, ServletException {
  87 + successHandler.onAuthenticationSuccess(request, response, authResult);
  88 + }
  89 +
  90 + @Override
  91 + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
  92 + AuthenticationException failed) throws IOException, ServletException {
  93 + SecurityContextHolder.clearContext();
  94 + failureHandler.onAuthenticationFailure(request, response, failed);
  95 + }
  96 +}
... ...
... ... @@ -30,6 +30,7 @@ public class SecurityUser extends User {
30 30
31 31 private Collection<GrantedAuthority> authorities;
32 32 private boolean enabled;
  33 + private UserPrincipal userPrincipal;
33 34
34 35 public SecurityUser() {
35 36 super();
... ... @@ -39,9 +40,10 @@ public class SecurityUser extends User {
39 40 super(id);
40 41 }
41 42
42   - public SecurityUser(User user, boolean enabled) {
  43 + public SecurityUser(User user, boolean enabled, UserPrincipal userPrincipal) {
43 44 super(user);
44 45 this.enabled = enabled;
  46 + this.userPrincipal = userPrincipal;
45 47 }
46 48
47 49 public Collection<? extends GrantedAuthority> getAuthorities() {
... ... @@ -57,8 +59,16 @@ public class SecurityUser extends User {
57 59 return enabled;
58 60 }
59 61
  62 + public UserPrincipal getUserPrincipal() {
  63 + return userPrincipal;
  64 + }
  65 +
60 66 public void setEnabled(boolean enabled) {
61 67 this.enabled = enabled;
62 68 }
63 69
  70 + public void setUserPrincipal(UserPrincipal userPrincipal) {
  71 + this.userPrincipal = userPrincipal;
  72 + }
  73 +
64 74 }
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package org.thingsboard.server.service.security.model;
  18 +
  19 +public class UserPrincipal {
  20 +
  21 + private final Type type;
  22 + private final String value;
  23 +
  24 + public UserPrincipal(Type type, String value) {
  25 + this.type = type;
  26 + this.value = value;
  27 + }
  28 +
  29 + public Type getType() {
  30 + return type;
  31 + }
  32 +
  33 + public String getValue() {
  34 + return value;
  35 + }
  36 +
  37 + public enum Type {
  38 + USER_NAME,
  39 + PUBLIC_ID
  40 + }
  41 +
  42 +}
... ...
... ... @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.id.UserId;
29 29 import org.thingsboard.server.common.data.security.Authority;
30 30 import org.thingsboard.server.config.JwtSettings;
31 31 import org.thingsboard.server.service.security.model.SecurityUser;
  32 +import org.thingsboard.server.service.security.model.UserPrincipal;
32 33
33 34 import java.util.Arrays;
34 35 import java.util.List;
... ... @@ -43,6 +44,7 @@ public class JwtTokenFactory {
43 44 private static final String FIRST_NAME = "firstName";
44 45 private static final String LAST_NAME = "lastName";
45 46 private static final String ENABLED = "enabled";
  47 + private static final String IS_PUBLIC = "isPublic";
46 48 private static final String TENANT_ID = "tenantId";
47 49 private static final String CUSTOMER_ID = "customerId";
48 50
... ... @@ -63,12 +65,15 @@ public class JwtTokenFactory {
63 65 if (securityUser.getAuthority() == null)
64 66 throw new IllegalArgumentException("User doesn't have any privileges");
65 67
66   - Claims claims = Jwts.claims().setSubject(securityUser.getEmail());
  68 + UserPrincipal principal = securityUser.getUserPrincipal();
  69 + String subject = principal.getValue();
  70 + Claims claims = Jwts.claims().setSubject(subject);
67 71 claims.put(SCOPES, securityUser.getAuthorities().stream().map(s -> s.getAuthority()).collect(Collectors.toList()));
68 72 claims.put(USER_ID, securityUser.getId().getId().toString());
69 73 claims.put(FIRST_NAME, securityUser.getFirstName());
70 74 claims.put(LAST_NAME, securityUser.getLastName());
71 75 claims.put(ENABLED, securityUser.isEnabled());
  76 + claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID);
72 77 if (securityUser.getTenantId() != null) {
73 78 claims.put(TENANT_ID, securityUser.getTenantId().getId().toString());
74 79 }
... ... @@ -104,6 +109,9 @@ public class JwtTokenFactory {
104 109 securityUser.setFirstName(claims.get(FIRST_NAME, String.class));
105 110 securityUser.setLastName(claims.get(LAST_NAME, String.class));
106 111 securityUser.setEnabled(claims.get(ENABLED, Boolean.class));
  112 + boolean isPublic = claims.get(IS_PUBLIC, Boolean.class);
  113 + UserPrincipal principal = new UserPrincipal(isPublic ? UserPrincipal.Type.PUBLIC_ID : UserPrincipal.Type.USER_NAME, subject);
  114 + securityUser.setUserPrincipal(principal);
107 115 String tenantId = claims.get(TENANT_ID, String.class);
108 116 if (tenantId != null) {
109 117 securityUser.setTenantId(new TenantId(UUID.fromString(tenantId)));
... ... @@ -123,9 +131,11 @@ public class JwtTokenFactory {
123 131
124 132 DateTime currentTime = new DateTime();
125 133
126   - Claims claims = Jwts.claims().setSubject(securityUser.getEmail());
  134 + UserPrincipal principal = securityUser.getUserPrincipal();
  135 + Claims claims = Jwts.claims().setSubject(principal.getValue());
127 136 claims.put(SCOPES, Arrays.asList(Authority.REFRESH_TOKEN.name()));
128 137 claims.put(USER_ID, securityUser.getId().getId().toString());
  138 + claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID);
129 139
130 140 String token = Jwts.builder()
131 141 .setClaims(claims)
... ... @@ -150,8 +160,10 @@ public class JwtTokenFactory {
150 160 if (!scopes.get(0).equals(Authority.REFRESH_TOKEN.name())) {
151 161 throw new IllegalArgumentException("Invalid Refresh Token scope");
152 162 }
  163 + boolean isPublic = claims.get(IS_PUBLIC, Boolean.class);
  164 + UserPrincipal principal = new UserPrincipal(isPublic ? UserPrincipal.Type.PUBLIC_ID : UserPrincipal.Type.USER_NAME, subject);
153 165 SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class))));
154   - securityUser.setEmail(subject);
  166 + securityUser.setUserPrincipal(principal);
155 167 return securityUser;
156 168 }
157 169
... ...
... ... @@ -60,8 +60,8 @@ plugins:
60 60
61 61 # JWT Token parameters
62 62 security.jwt:
63   - tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:900}" # Number of seconds (15 mins)
64   - refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:3600}" # Seconds (1 hour)
  63 + tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:9000000}" # Number of seconds (15 mins)
  64 + refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:36000000}" # Seconds (1 hour)
65 65 tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}"
66 66 tokenSigningKey: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}"
67 67
... ... @@ -189,11 +189,33 @@ cache:
189 189 updates:
190 190 # Enable/disable updates checking.
191 191 enabled: "${UPDATES_ENABLED:true}"
192   -
  192 +
  193 + # spring CORS configuration
  194 +spring.mvc.cors:
  195 + mappings:
  196 + # Intercept path
  197 + "/api/auth/**":
  198 + #Comma-separated list of origins to allow. '*' allows all origins. When not set,CORS support is disabled.
  199 + allowed-origins: "*"
  200 + #Comma-separated list of methods to allow. '*' allows all methods.
  201 + allowed-methods: "POST,GET,OPTIONS"
  202 + #Comma-separated list of headers to allow in a request. '*' allows all headers.
  203 + allowed-headers: "*"
  204 + #How long, in seconds, the response from a pre-flight request can be cached by clients.
  205 + max-age: "1800"
  206 + #Set whether credentials are supported. When not set, credentials are not supported.
  207 + allow-credentials: "true"
  208 + "/api/v1/**":
  209 + allowed-origins: "*"
  210 + allowed-methods: "*"
  211 + allowed-headers: "*"
  212 + max-age: "1800"
  213 + allow-credentials: "true"
  214 +
193 215 # SQL DAO Configuration
194 216 sql:
195 217 enabled: "${SQL_ENABLED:false}"
196 218 datasource:
197 219 url: "${SQL_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard}"
198 220 username: "${SQL_DATASOURCE_USERNAME:postgres}"
199   - password: "${SQL_DATASOURCE_PASSWORD:postgres}"
  221 + password: "${SQL_DATASOURCE_PASSWORD:postgres}"
... ...
... ... @@ -145,7 +145,7 @@ public class DefaultActorServiceTest {
145 145 ReflectionTestUtils.setField(actorContext, "eventService", eventService);
146 146
147 147
148   - when(routingService.resolve(any())).thenReturn(Optional.empty());
  148 + when(routingService.resolveById((EntityId) any())).thenReturn(Optional.empty());
149 149
150 150 when(discoveryService.getCurrentServer()).thenReturn(serverInstance);
151 151
... ... @@ -239,7 +239,7 @@ public class DefaultActorServiceTest {
239 239 List<TsKvEntry> expected = new ArrayList<>();
240 240 expected.add(new BasicTsKvEntry(ts, entry1));
241 241 expected.add(new BasicTsKvEntry(ts, entry2));
242   - verify(tsService, Mockito.timeout(5000)).save(DataConstants.DEVICE, deviceId, expected);
  242 + verify(tsService, Mockito.timeout(5000)).save(deviceId, expected);
243 243 }
244 244
245 245 }
... ...
... ... @@ -288,7 +288,7 @@ public class CustomerControllerTest extends AbstractControllerTest {
288 288 for (int i=0;i<143;i++) {
289 289 Customer customer = new Customer();
290 290 customer.setTenantId(tenantId);
291   - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15));
  291 + String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
292 292 String title = title1+suffix;
293 293 title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
294 294 customer.setTitle(title);
... ... @@ -299,7 +299,7 @@ public class CustomerControllerTest extends AbstractControllerTest {
299 299 for (int i=0;i<175;i++) {
300 300 Customer customer = new Customer();
301 301 customer.setTenantId(tenantId);
302   - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15));
  302 + String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
303 303 String title = title2+suffix;
304 304 title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
305 305 customer.setTitle(title);
... ...
... ... @@ -149,7 +149,7 @@ public class TenantControllerTest extends AbstractControllerTest {
149 149 List<Tenant> tenantsTitle1 = new ArrayList<>();
150 150 for (int i=0;i<134;i++) {
151 151 Tenant tenant = new Tenant();
152   - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15));
  152 + String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
153 153 String title = title1+suffix;
154 154 title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
155 155 tenant.setTitle(title);
... ... @@ -159,7 +159,7 @@ public class TenantControllerTest extends AbstractControllerTest {
159 159 List<Tenant> tenantsTitle2 = new ArrayList<>();
160 160 for (int i=0;i<127;i++) {
161 161 Tenant tenant = new Tenant();
162   - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15));
  162 + String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
163 163 String title = title2+suffix;
164 164 title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
165 165 tenant.setTitle(title);
... ...
... ... @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data;
19 19 * @author Andrew Shvayka
20 20 */
21 21 public enum EntityType {
22   - TENANT, DEVICE, CUSTOMER, RULE, PLUGIN
  22 + TENANT, CUSTOMER, USER, RULE, PLUGIN, DASHBOARD, ASSET, DEVICE, ALARM
23 23 }
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.alarm;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.AllArgsConstructor;
  20 +import lombok.Builder;
  21 +import lombok.Data;
  22 +import org.thingsboard.server.common.data.BaseData;
  23 +import org.thingsboard.server.common.data.id.AssetId;
  24 +import org.thingsboard.server.common.data.id.EntityId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +
  27 +/**
  28 + * Created by ashvayka on 11.05.17.
  29 + */
  30 +@Data
  31 +@Builder
  32 +@AllArgsConstructor
  33 +public class Alarm extends BaseData<AlarmId> {
  34 +
  35 + private TenantId tenantId;
  36 + private String type;
  37 + private EntityId originator;
  38 + private AlarmSeverity severity;
  39 + private AlarmStatus status;
  40 + private long startTs;
  41 + private long endTs;
  42 + private long ackTs;
  43 + private long clearTs;
  44 + private JsonNode details;
  45 + private boolean propagate;
  46 +
  47 + public Alarm() {
  48 + super();
  49 + }
  50 +
  51 + public Alarm(AlarmId id) {
  52 + super(id);
  53 + }
  54 +
  55 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.alarm;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonIgnore;
  20 +import com.fasterxml.jackson.annotation.JsonProperty;
  21 +import org.thingsboard.server.common.data.EntityType;
  22 +import org.thingsboard.server.common.data.id.EntityId;
  23 +import org.thingsboard.server.common.data.id.UUIDBased;
  24 +
  25 +import java.util.UUID;
  26 +
  27 +public class AlarmId extends UUIDBased implements EntityId {
  28 +
  29 + private static final long serialVersionUID = 1L;
  30 +
  31 + @JsonCreator
  32 + public AlarmId(@JsonProperty("id") UUID id) {
  33 + super(id);
  34 + }
  35 +
  36 + public static AlarmId fromString(String alarmId) {
  37 + return new AlarmId(UUID.fromString(alarmId));
  38 + }
  39 +
  40 + @JsonIgnore
  41 + @Override
  42 + public EntityType getEntityType() {
  43 + return EntityType.ALARM;
  44 + }
  45 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.alarm;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.page.TimePageLink;
  22 +
  23 +/**
  24 + * Created by ashvayka on 11.05.17.
  25 + */
  26 +@Data
  27 +public class AlarmQuery {
  28 +
  29 + private TenantId tenantId;
  30 + private EntityId affectedEntityId;
  31 + private TimePageLink pageLink;
  32 + private AlarmStatus status;
  33 +
  34 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.alarm;
  17 +
  18 +/**
  19 + * Created by ashvayka on 11.05.17.
  20 + */
  21 +public enum AlarmSeverity {
  22 +
  23 + CRITICAL, MAJOR, MINOR, WARNING, INDETERMINATE;
  24 +
  25 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.alarm;
  17 +
  18 +/**
  19 + * Created by ashvayka on 11.05.17.
  20 + */
  21 +public enum AlarmStatus {
  22 +
  23 + ACTIVE_UNACK, ACTIVE_ACK, CLEARED_UNACK, CLEARED_ACK;
  24 +
  25 + public boolean isAck() {
  26 + return this == ACTIVE_ACK || this == CLEARED_ACK;
  27 + }
  28 +
  29 + public boolean isCleared() {
  30 + return this == CLEARED_ACK || this == CLEARED_UNACK;
  31 + }
  32 +
  33 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.asset;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import org.thingsboard.server.common.data.SearchTextBased;
  20 +import org.thingsboard.server.common.data.id.AssetId;
  21 +import org.thingsboard.server.common.data.id.CustomerId;
  22 +import org.thingsboard.server.common.data.id.TenantId;
  23 +
  24 +public class Asset extends SearchTextBased<AssetId> {
  25 +
  26 + private static final long serialVersionUID = 2807343040519543363L;
  27 +
  28 + private TenantId tenantId;
  29 + private CustomerId customerId;
  30 + private String name;
  31 + private String type;
  32 + private JsonNode additionalInfo;
  33 +
  34 + public Asset() {
  35 + super();
  36 + }
  37 +
  38 + public Asset(AssetId id) {
  39 + super(id);
  40 + }
  41 +
  42 + public Asset(Asset asset) {
  43 + super(asset);
  44 + this.tenantId = asset.getTenantId();
  45 + this.customerId = asset.getCustomerId();
  46 + this.name = asset.getName();
  47 + this.type = asset.getType();
  48 + this.additionalInfo = asset.getAdditionalInfo();
  49 + }
  50 +
  51 + public TenantId getTenantId() {
  52 + return tenantId;
  53 + }
  54 +
  55 + public void setTenantId(TenantId tenantId) {
  56 + this.tenantId = tenantId;
  57 + }
  58 +
  59 + public CustomerId getCustomerId() {
  60 + return customerId;
  61 + }
  62 +
  63 + public void setCustomerId(CustomerId customerId) {
  64 + this.customerId = customerId;
  65 + }
  66 +
  67 + public String getName() {
  68 + return name;
  69 + }
  70 +
  71 + public void setName(String name) {
  72 + this.name = name;
  73 + }
  74 +
  75 + public String getType() {
  76 + return type;
  77 + }
  78 +
  79 + public void setType(String type) {
  80 + this.type = type;
  81 + }
  82 +
  83 + public JsonNode getAdditionalInfo() {
  84 + return additionalInfo;
  85 + }
  86 +
  87 + public void setAdditionalInfo(JsonNode additionalInfo) {
  88 + this.additionalInfo = additionalInfo;
  89 + }
  90 +
  91 + @Override
  92 + public String getSearchText() {
  93 + return name;
  94 + }
  95 +
  96 + @Override
  97 + public int hashCode() {
  98 + final int prime = 31;
  99 + int result = super.hashCode();
  100 + result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode());
  101 + result = prime * result + ((customerId == null) ? 0 : customerId.hashCode());
  102 + result = prime * result + ((name == null) ? 0 : name.hashCode());
  103 + result = prime * result + ((type == null) ? 0 : type.hashCode());
  104 + result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
  105 + return result;
  106 + }
  107 +
  108 + @Override
  109 + public boolean equals(Object obj) {
  110 + if (this == obj)
  111 + return true;
  112 + if (!super.equals(obj))
  113 + return false;
  114 + if (getClass() != obj.getClass())
  115 + return false;
  116 + Asset other = (Asset) obj;
  117 + if (additionalInfo == null) {
  118 + if (other.additionalInfo != null)
  119 + return false;
  120 + } else if (!additionalInfo.equals(other.additionalInfo))
  121 + return false;
  122 + if (customerId == null) {
  123 + if (other.customerId != null)
  124 + return false;
  125 + } else if (!customerId.equals(other.customerId))
  126 + return false;
  127 + if (name == null) {
  128 + if (other.name != null)
  129 + return false;
  130 + } else if (!name.equals(other.name))
  131 + return false;
  132 + if (type == null) {
  133 + if (other.type != null)
  134 + return false;
  135 + } else if (!type.equals(other.type))
  136 + return false;
  137 + if (tenantId == null) {
  138 + if (other.tenantId != null)
  139 + return false;
  140 + } else if (!tenantId.equals(other.tenantId))
  141 + return false;
  142 + return true;
  143 + }
  144 +
  145 + @Override
  146 + public String toString() {
  147 + StringBuilder builder = new StringBuilder();
  148 + builder.append("Asset [tenantId=");
  149 + builder.append(tenantId);
  150 + builder.append(", customerId=");
  151 + builder.append(customerId);
  152 + builder.append(", name=");
  153 + builder.append(name);
  154 + builder.append(", type=");
  155 + builder.append(type);
  156 + builder.append(", additionalInfo=");
  157 + builder.append(additionalInfo);
  158 + builder.append(", createdTime=");
  159 + builder.append(createdTime);
  160 + builder.append(", id=");
  161 + builder.append(id);
  162 + builder.append("]");
  163 + return builder.toString();
  164 + }
  165 +
  166 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.id;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonIgnore;
  20 +import com.fasterxml.jackson.annotation.JsonProperty;
  21 +import org.thingsboard.server.common.data.EntityType;
  22 +
  23 +import java.util.UUID;
  24 +
  25 +public class AssetId extends UUIDBased implements EntityId {
  26 +
  27 + private static final long serialVersionUID = 1L;
  28 +
  29 + @JsonCreator
  30 + public AssetId(@JsonProperty("id") UUID id) {
  31 + super(id);
  32 + }
  33 +
  34 + public static AssetId fromString(String assetId) {
  35 + return new AssetId(UUID.fromString(assetId));
  36 + }
  37 +
  38 + @JsonIgnore
  39 + @Override
  40 + public EntityType getEntityType() {
  41 + return EntityType.ASSET;
  42 + }
  43 +}
... ...
... ... @@ -18,12 +18,24 @@ package org.thingsboard.server.common.data.id;
18 18 import java.util.UUID;
19 19
20 20 import com.fasterxml.jackson.annotation.JsonCreator;
  21 +import com.fasterxml.jackson.annotation.JsonIgnore;
21 22 import com.fasterxml.jackson.annotation.JsonProperty;
  23 +import org.thingsboard.server.common.data.EntityType;
22 24
23   -public class DashboardId extends UUIDBased {
  25 +public class DashboardId extends UUIDBased implements EntityId {
24 26
25 27 @JsonCreator
26   - public DashboardId(@JsonProperty("id") UUID id){
  28 + public DashboardId(@JsonProperty("id") UUID id) {
27 29 super(id);
28 30 }
  31 +
  32 + public static DashboardId fromString(String dashboardId) {
  33 + return new DashboardId(UUID.fromString(dashboardId));
  34 + }
  35 +
  36 + @JsonIgnore
  37 + @Override
  38 + public EntityType getEntityType() {
  39 + return EntityType.DASHBOARD;
  40 + }
29 41 }
... ...
... ... @@ -16,6 +16,8 @@
16 16 package org.thingsboard.server.common.data.id;
17 17
18 18 import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
  20 +import com.fasterxml.jackson.databind.annotation.JsonSerialize;
19 21 import org.thingsboard.server.common.data.EntityType;
20 22
21 23 import java.util.UUID;
... ... @@ -23,6 +25,9 @@ import java.util.UUID;
23 25 /**
24 26 * @author Andrew Shvayka
25 27 */
  28 +
  29 +@JsonDeserialize(using = EntityIdDeserializer.class)
  30 +@JsonSerialize(using = EntityIdSerializer.class)
26 31 public interface EntityId {
27 32
28 33 UUID NULL_UUID = UUID.fromString("13814000-1dd2-11b2-8080-808080808080");
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.id;
  17 +
  18 +import com.fasterxml.jackson.core.JsonParser;
  19 +import com.fasterxml.jackson.core.JsonProcessingException;
  20 +import com.fasterxml.jackson.core.ObjectCodec;
  21 +import com.fasterxml.jackson.databind.DeserializationContext;
  22 +import com.fasterxml.jackson.databind.JsonDeserializer;
  23 +import com.fasterxml.jackson.databind.node.ObjectNode;
  24 +
  25 +import java.io.IOException;
  26 +
  27 +/**
  28 + * Created by ashvayka on 11.05.17.
  29 + */
  30 +public class EntityIdDeserializer extends JsonDeserializer<EntityId> {
  31 +
  32 + @Override
  33 + public EntityId deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException, JsonProcessingException {
  34 + ObjectCodec oc = jsonParser.getCodec();
  35 + ObjectNode node = oc.readTree(jsonParser);
  36 + if (node.has("entityType") && node.has("id")) {
  37 + return EntityIdFactory.getByTypeAndId(node.get("entityType").asText(), node.get("id").asText());
  38 + } else {
  39 + throw new IOException("Missing entityType or id!");
  40 + }
  41 + }
  42 +
  43 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.id;
  17 +
  18 +import org.thingsboard.server.common.data.EntityType;
  19 +
  20 +import java.util.UUID;
  21 +
  22 +/**
  23 + * Created by ashvayka on 25.04.17.
  24 + */
  25 +public class EntityIdFactory {
  26 +
  27 + public static EntityId getByTypeAndId(String type, String uuid) {
  28 + return getByTypeAndUuid(EntityType.valueOf(type), UUID.fromString(uuid));
  29 + }
  30 +
  31 + public static EntityId getByTypeAndUuid(String type, UUID uuid) {
  32 + return getByTypeAndUuid(EntityType.valueOf(type), uuid);
  33 + }
  34 +
  35 + public static EntityId getByTypeAndUuid(EntityType type, UUID uuid) {
  36 + switch (type) {
  37 + case TENANT:
  38 + return new TenantId(uuid);
  39 + case CUSTOMER:
  40 + return new CustomerId(uuid);
  41 + case USER:
  42 + return new UserId(uuid);
  43 + case RULE:
  44 + return new RuleId(uuid);
  45 + case PLUGIN:
  46 + return new PluginId(uuid);
  47 + case DASHBOARD:
  48 + return new DashboardId(uuid);
  49 + case DEVICE:
  50 + return new DeviceId(uuid);
  51 + case ASSET:
  52 + return new AssetId(uuid);
  53 + }
  54 + throw new IllegalArgumentException("EntityType " + type + " is not supported!");
  55 + }
  56 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.id;
  17 +
  18 +import com.fasterxml.jackson.core.JsonGenerator;
  19 +import com.fasterxml.jackson.core.JsonProcessingException;
  20 +import com.fasterxml.jackson.databind.JsonSerializer;
  21 +import com.fasterxml.jackson.databind.SerializerProvider;
  22 +
  23 +import java.io.IOException;
  24 +
  25 +/**
  26 + * Created by ashvayka on 11.05.17.
  27 + */
  28 +public class EntityIdSerializer extends JsonSerializer<EntityId> {
  29 +
  30 + @Override
  31 + public void serialize(EntityId value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
  32 + gen.writeStartObject();
  33 + gen.writeStringField("entityType", value.getEntityType().name());
  34 + gen.writeStringField("id", value.getId().toString());
  35 + gen.writeEndObject();
  36 + }
  37 +}
... ...
... ... @@ -18,12 +18,25 @@ package org.thingsboard.server.common.data.id;
18 18 import java.util.UUID;
19 19
20 20 import com.fasterxml.jackson.annotation.JsonCreator;
  21 +import com.fasterxml.jackson.annotation.JsonIgnore;
21 22 import com.fasterxml.jackson.annotation.JsonProperty;
  23 +import org.thingsboard.server.common.data.EntityType;
22 24
23   -public class UserId extends UUIDBased {
  25 +public class UserId extends UUIDBased implements EntityId {
24 26
25 27 @JsonCreator
26   - public UserId(@JsonProperty("id") UUID id){
27   - super(id);
28   - }
  28 + public UserId(@JsonProperty("id") UUID id) {
  29 + super(id);
  30 + }
  31 +
  32 + public static UserId fromString(String userId) {
  33 + return new UserId(UUID.fromString(userId));
  34 + }
  35 +
  36 + @JsonIgnore
  37 + @Override
  38 + public EntityType getEntityType() {
  39 + return EntityType.USER;
  40 + }
  41 +
29 42 }
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.relation;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +
  21 +import java.util.Objects;
  22 +
  23 +public class EntityRelation {
  24 +
  25 + private static final long serialVersionUID = 2807343040519543363L;
  26 +
  27 + public static final String CONTAINS_TYPE = "Contains";
  28 + public static final String MANAGES_TYPE = "Manages";
  29 +
  30 + private EntityId from;
  31 + private EntityId to;
  32 + private String type;
  33 + private JsonNode additionalInfo;
  34 +
  35 + public EntityRelation() {
  36 + super();
  37 + }
  38 +
  39 + public EntityRelation(EntityId from, EntityId to, String type) {
  40 + this(from, to, type, null);
  41 + }
  42 +
  43 + public EntityRelation(EntityId from, EntityId to, String type, JsonNode additionalInfo) {
  44 + this.from = from;
  45 + this.to = to;
  46 + this.type = type;
  47 + this.additionalInfo = additionalInfo;
  48 + }
  49 +
  50 + public EntityRelation(EntityRelation device) {
  51 + this.from = device.getFrom();
  52 + this.to = device.getTo();
  53 + this.type = device.getType();
  54 + this.additionalInfo = device.getAdditionalInfo();
  55 + }
  56 +
  57 + public EntityId getFrom() {
  58 + return from;
  59 + }
  60 +
  61 + public void setFrom(EntityId from) {
  62 + this.from = from;
  63 + }
  64 +
  65 + public EntityId getTo() {
  66 + return to;
  67 + }
  68 +
  69 + public void setTo(EntityId to) {
  70 + this.to = to;
  71 + }
  72 +
  73 + public String getType() {
  74 + return type;
  75 + }
  76 +
  77 + public void setType(String type) {
  78 + this.type = type;
  79 + }
  80 +
  81 + public JsonNode getAdditionalInfo() {
  82 + return additionalInfo;
  83 + }
  84 +
  85 + public void setAdditionalInfo(JsonNode additionalInfo) {
  86 + this.additionalInfo = additionalInfo;
  87 + }
  88 +
  89 + @Override
  90 + public boolean equals(Object o) {
  91 + if (this == o) return true;
  92 + if (o == null || getClass() != o.getClass()) return false;
  93 + EntityRelation relation = (EntityRelation) o;
  94 + return Objects.equals(from, relation.from) &&
  95 + Objects.equals(to, relation.to) &&
  96 + Objects.equals(type, relation.type);
  97 + }
  98 +
  99 + @Override
  100 + public int hashCode() {
  101 + return Objects.hash(from, to, type);
  102 + }
  103 +}
... ...
... ... @@ -187,4 +187,14 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte
187 187 List<E> entities = findListByStatement(QueryBuilder.select().all().from(getColumnFamilyName()).setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()));
188 188 return DaoUtil.convertDataList(entities);
189 189 }
  190 +
  191 + protected static <T> Function<BaseEntity<T>, T> toDataFunction() {
  192 + return new Function<BaseEntity<T>, T>() {
  193 + @Nullable
  194 + @Override
  195 + public T apply(@Nullable BaseEntity<T> entity) {
  196 + return entity != null ? entity.toData() : null;
  197 + }
  198 + };
  199 + }
190 200 }
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.alarm;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.alarm.Alarm;
  20 +import org.thingsboard.server.common.data.id.EntityId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
  22 +import org.thingsboard.server.dao.Dao;
  23 +import org.thingsboard.server.dao.model.AlarmEntity;
  24 +
  25 +import java.util.UUID;
  26 +
  27 +/**
  28 + * Created by ashvayka on 11.05.17.
  29 + */
  30 +public interface AlarmDao extends Dao<Alarm> {
  31 +
  32 + ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);
  33 +
  34 + ListenableFuture<Alarm> findAlarmByIdAsync(UUID key);
  35 +
  36 + Alarm save(Alarm alarm);
  37 +
  38 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.alarm;
  17 +
  18 +import com.datastax.driver.core.querybuilder.QueryBuilder;
  19 +import com.datastax.driver.core.querybuilder.Select;
  20 +import com.google.common.util.concurrent.ListenableFuture;
  21 +import lombok.extern.slf4j.Slf4j;
  22 +import org.springframework.stereotype.Component;
  23 +import org.thingsboard.server.common.data.alarm.Alarm;
  24 +import org.thingsboard.server.common.data.id.EntityId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.dao.CassandraAbstractModelDao;
  27 +import org.thingsboard.server.dao.model.AlarmEntity;
  28 +import org.thingsboard.server.dao.model.ModelConstants;
  29 +
  30 +import java.util.UUID;
  31 +
  32 +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
  33 +import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
  34 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  35 +
  36 +@Component
  37 +@Slf4j
  38 +public class AlarmDaoImpl extends CassandraAbstractModelDao<AlarmEntity, Alarm> implements AlarmDao {
  39 +
  40 + @Override
  41 + protected Class<AlarmEntity> getColumnFamilyClass() {
  42 + return AlarmEntity.class;
  43 + }
  44 +
  45 + @Override
  46 + protected String getColumnFamilyName() {
  47 + return ALARM_COLUMN_FAMILY_NAME;
  48 + }
  49 +
  50 + @Override
  51 + public Alarm save(Alarm alarm) {
  52 + log.debug("Save asset [{}] ", alarm);
  53 + return super.save(alarm);
  54 + }
  55 +
  56 + @Override
  57 + public ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) {
  58 + Select select = select().from(ALARM_COLUMN_FAMILY_NAME);
  59 + Select.Where query = select.where();
  60 + query.and(eq(ALARM_TENANT_ID_PROPERTY, tenantId.getId()));
  61 + query.and(eq(ALARM_ORIGINATOR_ID_PROPERTY, originator.getId()));
  62 + query.and(eq(ALARM_ORIGINATOR_TYPE_PROPERTY, originator.getEntityType()));
  63 + query.and(eq(ALARM_TYPE_PROPERTY, type));
  64 + query.limit(1);
  65 + query.orderBy(QueryBuilder.asc(ModelConstants.ALARM_TYPE_PROPERTY), QueryBuilder.desc(ModelConstants.ID_PROPERTY));
  66 + return findOneByStatementAsync(query);
  67 + }
  68 +
  69 + @Override
  70 + public ListenableFuture<Alarm> findAlarmByIdAsync(UUID key) {
  71 + log.debug("Get alarm by id {}", key);
  72 + Select.Where query = select().from(ALARM_BY_ID_VIEW_NAME).where(eq(ModelConstants.ID_PROPERTY, key));
  73 + query.limit(1);
  74 + log.trace("Execute query {}", query);
  75 + return findOneByStatementAsync(query);
  76 + }
  77 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.alarm;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.Device;
  20 +import org.thingsboard.server.common.data.alarm.Alarm;
  21 +import org.thingsboard.server.common.data.alarm.AlarmId;
  22 +import org.thingsboard.server.common.data.alarm.AlarmQuery;
  23 +import org.thingsboard.server.common.data.id.DeviceId;
  24 +import org.thingsboard.server.common.data.id.EntityId;
  25 +import org.thingsboard.server.common.data.page.TimePageData;
  26 +
  27 +import java.util.Optional;
  28 +
  29 +/**
  30 + * Created by ashvayka on 11.05.17.
  31 + */
  32 +public interface AlarmService {
  33 +
  34 + Alarm createOrUpdateAlarm(Alarm alarm);
  35 +
  36 + ListenableFuture<Boolean> updateAlarm(Alarm alarm);
  37 +
  38 + ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTs);
  39 +
  40 + ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, long ackTs);
  41 +
  42 + ListenableFuture<Alarm> findAlarmById(AlarmId alarmId);
  43 +
  44 + ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query);
  45 +
  46 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.alarm;
  17 +
  18 +
  19 +import com.google.common.base.Function;
  20 +import com.google.common.util.concurrent.Futures;
  21 +import com.google.common.util.concurrent.ListenableFuture;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.springframework.beans.factory.annotation.Autowired;
  24 +import org.springframework.stereotype.Service;
  25 +import org.springframework.util.StringUtils;
  26 +import org.thingsboard.server.common.data.Tenant;
  27 +import org.thingsboard.server.common.data.alarm.Alarm;
  28 +import org.thingsboard.server.common.data.alarm.AlarmId;
  29 +import org.thingsboard.server.common.data.alarm.AlarmQuery;
  30 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  31 +import org.thingsboard.server.common.data.id.EntityId;
  32 +import org.thingsboard.server.common.data.page.TimePageData;
  33 +import org.thingsboard.server.common.data.relation.EntityRelation;
  34 +import org.thingsboard.server.dao.entity.BaseEntityService;
  35 +import org.thingsboard.server.dao.exception.DataValidationException;
  36 +import org.thingsboard.server.dao.relation.EntityRelationsQuery;
  37 +import org.thingsboard.server.dao.relation.EntitySearchDirection;
  38 +import org.thingsboard.server.dao.relation.RelationService;
  39 +import org.thingsboard.server.dao.relation.RelationsSearchParameters;
  40 +import org.thingsboard.server.dao.service.DataValidator;
  41 +import org.thingsboard.server.dao.tenant.TenantDao;
  42 +
  43 +import javax.annotation.Nullable;
  44 +import java.util.List;
  45 +import java.util.concurrent.ExecutionException;
  46 +import java.util.stream.Collectors;
  47 +
  48 +import static org.thingsboard.server.dao.service.Validator.validateId;
  49 +
  50 +@Service
  51 +@Slf4j
  52 +public class BaseAlarmService extends BaseEntityService implements AlarmService {
  53 +
  54 + private static final String ALARM_RELATION_PREFIX = "ALARM_";
  55 + private static final String ALARM_RELATION = "ALARM_ANY";
  56 +
  57 + @Autowired
  58 + private AlarmDao alarmDao;
  59 +
  60 + @Autowired
  61 + private TenantDao tenantDao;
  62 +
  63 + @Autowired
  64 + private RelationService relationService;
  65 +
  66 + @Override
  67 + public Alarm createOrUpdateAlarm(Alarm alarm) {
  68 + alarmDataValidator.validate(alarm);
  69 + try {
  70 + if (alarm.getStartTs() == 0L) {
  71 + alarm.setStartTs(System.currentTimeMillis());
  72 + }
  73 + if (alarm.getEndTs() == 0L) {
  74 + alarm.setEndTs(alarm.getStartTs());
  75 + }
  76 + Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get();
  77 + if (existing == null || existing.getStatus().isCleared()) {
  78 + log.debug("New Alarm : {}", alarm);
  79 + Alarm saved = alarmDao.save(alarm);
  80 + EntityRelationsQuery query = new EntityRelationsQuery();
  81 + query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
  82 + List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
  83 + for (EntityId parentId : parentEntities) {
  84 + createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION));
  85 + createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name()));
  86 + }
  87 + return saved;
  88 + } else {
  89 + log.debug("Alarm before merge: {}", alarm);
  90 + alarm = merge(existing, alarm);
  91 + log.debug("Alarm after merge: {}", alarm);
  92 + return alarmDao.save(alarm);
  93 + }
  94 + } catch (ExecutionException | InterruptedException e) {
  95 + throw new RuntimeException(e);
  96 + }
  97 + }
  98 +
  99 + @Override
  100 + public ListenableFuture<Boolean> updateAlarm(Alarm update) {
  101 + alarmDataValidator.validate(update);
  102 + return getAndUpdate(update.getId(), new Function<Alarm, Boolean>() {
  103 + @Nullable
  104 + @Override
  105 + public Boolean apply(@Nullable Alarm alarm) {
  106 + if (alarm == null) {
  107 + return false;
  108 + } else {
  109 + AlarmStatus oldStatus = alarm.getStatus();
  110 + AlarmStatus newStatus = update.getStatus();
  111 + alarmDao.save(merge(alarm, update));
  112 + if (oldStatus != newStatus) {
  113 + updateRelations(alarm, oldStatus, newStatus);
  114 + }
  115 + return true;
  116 + }
  117 + }
  118 + });
  119 + }
  120 +
  121 + @Override
  122 + public ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTime) {
  123 + return getAndUpdate(alarmId, new Function<Alarm, Boolean>() {
  124 + @Nullable
  125 + @Override
  126 + public Boolean apply(@Nullable Alarm alarm) {
  127 + if (alarm == null || alarm.getStatus().isAck()) {
  128 + return false;
  129 + } else {
  130 + AlarmStatus oldStatus = alarm.getStatus();
  131 + AlarmStatus newStatus = oldStatus.isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK;
  132 + alarm.setStatus(newStatus);
  133 + alarm.setAckTs(ackTime);
  134 + alarmDao.save(alarm);
  135 + updateRelations(alarm, oldStatus, newStatus);
  136 + return true;
  137 + }
  138 + }
  139 + });
  140 + }
  141 +
  142 + @Override
  143 + public ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, long clearTime) {
  144 + return getAndUpdate(alarmId, new Function<Alarm, Boolean>() {
  145 + @Nullable
  146 + @Override
  147 + public Boolean apply(@Nullable Alarm alarm) {
  148 + if (alarm == null || alarm.getStatus().isCleared()) {
  149 + return false;
  150 + } else {
  151 + AlarmStatus oldStatus = alarm.getStatus();
  152 + AlarmStatus newStatus = oldStatus.isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK;
  153 + alarm.setStatus(newStatus);
  154 + alarm.setClearTs(clearTime);
  155 + alarmDao.save(alarm);
  156 + updateRelations(alarm, oldStatus, newStatus);
  157 + return true;
  158 + }
  159 + }
  160 + });
  161 + }
  162 +
  163 + @Override
  164 + public ListenableFuture<Alarm> findAlarmById(AlarmId alarmId) {
  165 + log.trace("Executing findAlarmById [{}]", alarmId);
  166 + validateId(alarmId, "Incorrect alarmId " + alarmId);
  167 + return alarmDao.findAlarmByIdAsync(alarmId.getId());
  168 + }
  169 +
  170 + @Override
  171 + public ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query) {
  172 + return null;
  173 + }
  174 +
  175 + private void deleteRelation(EntityRelation alarmRelation) throws ExecutionException, InterruptedException {
  176 + log.debug("Deleting Alarm relation: {}", alarmRelation);
  177 + relationService.deleteRelation(alarmRelation).get();
  178 + }
  179 +
  180 + private void createRelation(EntityRelation alarmRelation) throws ExecutionException, InterruptedException {
  181 + log.debug("Creating Alarm relation: {}", alarmRelation);
  182 + relationService.saveRelation(alarmRelation).get();
  183 + }
  184 +
  185 + private Alarm merge(Alarm existing, Alarm alarm) {
  186 + if (alarm.getStartTs() > existing.getEndTs()) {
  187 + existing.setEndTs(alarm.getStartTs());
  188 + }
  189 + if (alarm.getEndTs() > existing.getEndTs()) {
  190 + existing.setEndTs(alarm.getEndTs());
  191 + }
  192 + if (alarm.getClearTs() > existing.getClearTs()) {
  193 + existing.setClearTs(alarm.getClearTs());
  194 + }
  195 + if (alarm.getAckTs() > existing.getAckTs()) {
  196 + existing.setAckTs(alarm.getAckTs());
  197 + }
  198 + existing.setStatus(alarm.getStatus());
  199 + existing.setSeverity(alarm.getSeverity());
  200 + existing.setDetails(alarm.getDetails());
  201 + return existing;
  202 + }
  203 +
  204 + private void updateRelations(Alarm alarm, AlarmStatus oldStatus, AlarmStatus newStatus) {
  205 + try {
  206 + EntityRelationsQuery query = new EntityRelationsQuery();
  207 + query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
  208 + List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
  209 + for (EntityId parentId : parentEntities) {
  210 + deleteRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name()));
  211 + createRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name()));
  212 + }
  213 + } catch (ExecutionException | InterruptedException e) {
  214 + log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus);
  215 + throw new RuntimeException(e);
  216 + }
  217 + }
  218 +
  219 + private ListenableFuture<Boolean> getAndUpdate(AlarmId alarmId, Function<Alarm, Boolean> function) {
  220 + validateId(alarmId, "Alarm id should be specified!");
  221 + ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId());
  222 + return Futures.transform(entity, function);
  223 + }
  224 +
  225 + private DataValidator<Alarm> alarmDataValidator =
  226 + new DataValidator<Alarm>() {
  227 +
  228 + @Override
  229 + protected void validateDataImpl(Alarm alarm) {
  230 + if (StringUtils.isEmpty(alarm.getType())) {
  231 + throw new DataValidationException("Alarm type should be specified!");
  232 + }
  233 + if (alarm.getOriginator() == null) {
  234 + throw new DataValidationException("Alarm originator should be specified!");
  235 + }
  236 + if (alarm.getSeverity() == null) {
  237 + throw new DataValidationException("Alarm severity should be specified!");
  238 + }
  239 + if (alarm.getStatus() == null) {
  240 + throw new DataValidationException("Alarm status should be specified!");
  241 + }
  242 + if (alarm.getTenantId() == null) {
  243 + throw new DataValidationException("Alarm should be assigned to tenant!");
  244 + } else {
  245 + Tenant tenant = tenantDao.findById(alarm.getTenantId().getId());
  246 + if (tenant == null) {
  247 + throw new DataValidationException("Alarm is referencing to non-existent tenant!");
  248 + }
  249 + }
  250 + }
  251 + };
  252 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.asset;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.asset.Asset;
  20 +import org.thingsboard.server.common.data.page.TextPageLink;
  21 +import org.thingsboard.server.dao.Dao;
  22 +
  23 +import java.util.List;
  24 +import java.util.Optional;
  25 +import java.util.UUID;
  26 +
  27 +/**
  28 + * The Interface AssetDao.
  29 + *
  30 + */
  31 +public interface AssetDao extends Dao<Asset> {
  32 +
  33 + /**
  34 + * Save or update asset object
  35 + *
  36 + * @param asset the asset object
  37 + * @return saved asset object
  38 + */
  39 + Asset save(Asset asset);
  40 +
  41 + /**
  42 + * Find assets by tenantId and page link.
  43 + *
  44 + * @param tenantId the tenantId
  45 + * @param pageLink the page link
  46 + * @return the list of asset objects
  47 + */
  48 + List<Asset> findAssetsByTenantId(UUID tenantId, TextPageLink pageLink);
  49 +
  50 + /**
  51 + * Find assets by tenantId and assets Ids.
  52 + *
  53 + * @param tenantId the tenantId
  54 + * @param assetIds the asset Ids
  55 + * @return the list of asset objects
  56 + */
  57 + ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(UUID tenantId, List<UUID> assetIds);
  58 +
  59 + /**
  60 + * Find assets by tenantId, customerId and page link.
  61 + *
  62 + * @param tenantId the tenantId
  63 + * @param customerId the customerId
  64 + * @param pageLink the page link
  65 + * @return the list of asset objects
  66 + */
  67 + List<Asset> findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
  68 +
  69 + /**
  70 + * Find assets by tenantId, customerId and assets Ids.
  71 + *
  72 + * @param tenantId the tenantId
  73 + * @param customerId the customerId
  74 + * @param assetIds the asset Ids
  75 + * @return the list of asset objects
  76 + */
  77 + ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List<UUID> assetIds);
  78 +
  79 + /**
  80 + * Find assets by tenantId and asset name.
  81 + *
  82 + * @param tenantId the tenantId
  83 + * @param name the asset name
  84 + * @return the optional asset object
  85 + */
  86 + Optional<Asset> findAssetsByTenantIdAndName(UUID tenantId, String name);
  87 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.asset;
  17 +
  18 +import com.datastax.driver.core.querybuilder.Select;
  19 +import com.google.common.util.concurrent.ListenableFuture;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.stereotype.Component;
  22 +import org.thingsboard.server.common.data.asset.Asset;
  23 +import org.thingsboard.server.common.data.page.TextPageLink;
  24 +import org.thingsboard.server.dao.CassandraAbstractSearchTextDao;
  25 +import org.thingsboard.server.dao.DaoUtil;
  26 +import org.thingsboard.server.dao.model.AssetEntity;
  27 +
  28 +import java.util.*;
  29 +
  30 +import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
  31 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  32 +
  33 +@Component
  34 +@Slf4j
  35 +public class AssetDaoImpl extends CassandraAbstractSearchTextDao<AssetEntity, Asset> implements AssetDao {
  36 +
  37 + @Override
  38 + protected Class<AssetEntity> getColumnFamilyClass() {
  39 + return AssetEntity.class;
  40 + }
  41 +
  42 + @Override
  43 + protected String getColumnFamilyName() {
  44 + return ASSET_COLUMN_FAMILY_NAME;
  45 + }
  46 +
  47 + @Override
  48 + public Asset save(Asset asset) {
  49 + log.debug("Save asset [{}] ", asset);
  50 + return save(asset);
  51 + }
  52 +
  53 + @Override
  54 + public List<Asset> findAssetsByTenantId(UUID tenantId, TextPageLink pageLink) {
  55 + log.debug("Try to find assets by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
  56 + List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
  57 + Collections.singletonList(eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink);
  58 +
  59 + log.trace("Found assets [{}] by tenantId [{}] and pageLink [{}]", assetEntities, tenantId, pageLink);
  60 + return DaoUtil.convertDataList(assetEntities);
  61 + }
  62 +
  63 + @Override
  64 + public ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(UUID tenantId, List<UUID> assetIds) {
  65 + log.debug("Try to find assets by tenantId [{}] and asset Ids [{}]", tenantId, assetIds);
  66 + Select select = select().from(getColumnFamilyName());
  67 + Select.Where query = select.where();
  68 + query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
  69 + query.and(in(ID_PROPERTY, assetIds));
  70 + return findListByStatementAsync(query);
  71 + }
  72 +
  73 + @Override
  74 + public List<Asset> findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
  75 + log.debug("Try to find assets by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
  76 + List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
  77 + Arrays.asList(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
  78 + eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
  79 + pageLink);
  80 +
  81 + log.trace("Found assets [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", assetEntities, tenantId, customerId, pageLink);
  82 + return DaoUtil.convertDataList(assetEntities);
  83 + }
  84 +
  85 + @Override
  86 + public ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List<UUID> assetIds) {
  87 + log.debug("Try to find assets by tenantId [{}], customerId [{}] and asset Ids [{}]", tenantId, customerId, assetIds);
  88 + Select select = select().from(getColumnFamilyName());
  89 + Select.Where query = select.where();
  90 + query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
  91 + query.and(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId));
  92 + query.and(in(ID_PROPERTY, assetIds));
  93 + return findListByStatementAsync(query);
  94 + }
  95 +
  96 + @Override
  97 + public Optional<Asset> findAssetsByTenantIdAndName(UUID tenantId, String assetName) {
  98 + Select select = select().from(ASSET_BY_TENANT_AND_NAME_VIEW_NAME);
  99 + Select.Where query = select.where();
  100 + query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
  101 + query.and(eq(ASSET_NAME_PROPERTY, assetName));
  102 + AssetEntity assetEntity = (AssetEntity) findOneByStatement(query);
  103 + return Optional.ofNullable(DaoUtil.getData(assetEntity));
  104 + }
  105 +
  106 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.asset;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.EntityType;
  20 +import org.thingsboard.server.common.data.relation.EntityRelation;
  21 +import org.thingsboard.server.dao.relation.RelationsSearchParameters;
  22 +import org.thingsboard.server.dao.relation.EntityRelationsQuery;
  23 +import org.thingsboard.server.dao.relation.EntityTypeFilter;
  24 +
  25 +import javax.annotation.Nullable;
  26 +import java.util.ArrayList;
  27 +import java.util.Collections;
  28 +import java.util.List;
  29 +
  30 +/**
  31 + * Created by ashvayka on 03.05.17.
  32 + */
  33 +@Data
  34 +public class AssetSearchQuery {
  35 +
  36 + private RelationsSearchParameters parameters;
  37 + @Nullable
  38 + private String relationType;
  39 + @Nullable
  40 + private List<String> assetTypes;
  41 +
  42 + public EntityRelationsQuery toEntitySearchQuery() {
  43 + EntityRelationsQuery query = new EntityRelationsQuery();
  44 + query.setParameters(parameters);
  45 + query.setFilters(
  46 + Collections.singletonList(new EntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType,
  47 + Collections.singletonList(EntityType.ASSET))));
  48 + return query;
  49 + }
  50 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.asset;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.asset.Asset;
  20 +import org.thingsboard.server.common.data.id.AssetId;
  21 +import org.thingsboard.server.common.data.id.CustomerId;
  22 +import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.common.data.page.TextPageData;
  24 +import org.thingsboard.server.common.data.page.TextPageLink;
  25 +
  26 +import java.util.List;
  27 +import java.util.Optional;
  28 +
  29 +public interface AssetService {
  30 +
  31 + Asset findAssetById(AssetId assetId);
  32 +
  33 + ListenableFuture<Asset> findAssetByIdAsync(AssetId assetId);
  34 +
  35 + Optional<Asset> findAssetByTenantIdAndName(TenantId tenantId, String name);
  36 +
  37 + Asset saveAsset(Asset device);
  38 +
  39 + Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId);
  40 +
  41 + Asset unassignAssetFromCustomer(AssetId assetId);
  42 +
  43 + void deleteAsset(AssetId assetId);
  44 +
  45 + TextPageData<Asset> findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink);
  46 +
  47 + ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List<AssetId> assetIds);
  48 +
  49 + void deleteAssetsByTenantId(TenantId tenantId);
  50 +
  51 + TextPageData<Asset> findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
  52 +
  53 + ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<AssetId> assetIds);
  54 +
  55 + void unassignCustomerAssets(TenantId tenantId, CustomerId customerId);
  56 +
  57 + ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query);
  58 +
  59 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.asset;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import javax.annotation.Nullable;
  21 +import java.util.List;
  22 +
  23 +/**
  24 + * Created by ashvayka on 02.05.17.
  25 + */
  26 +@Data
  27 +public class AssetTypeFilter {
  28 + @Nullable
  29 + private String relationType;
  30 + @Nullable
  31 + private List<String> assetTypes;
  32 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.asset;
  17 +
  18 +
  19 +import com.google.common.base.Function;
  20 +import com.google.common.util.concurrent.AsyncFunction;
  21 +import com.google.common.util.concurrent.Futures;
  22 +import com.google.common.util.concurrent.ListenableFuture;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.springframework.stereotype.Service;
  26 +import org.springframework.util.StringUtils;
  27 +import org.thingsboard.server.common.data.Customer;
  28 +import org.thingsboard.server.common.data.EntityType;
  29 +import org.thingsboard.server.common.data.Tenant;
  30 +import org.thingsboard.server.common.data.asset.Asset;
  31 +import org.thingsboard.server.common.data.id.AssetId;
  32 +import org.thingsboard.server.common.data.id.CustomerId;
  33 +import org.thingsboard.server.common.data.id.EntityId;
  34 +import org.thingsboard.server.common.data.id.TenantId;
  35 +import org.thingsboard.server.common.data.page.TextPageData;
  36 +import org.thingsboard.server.common.data.page.TextPageLink;
  37 +import org.thingsboard.server.common.data.relation.EntityRelation;
  38 +import org.thingsboard.server.dao.customer.CustomerDao;
  39 +import org.thingsboard.server.dao.entity.BaseEntityService;
  40 +import org.thingsboard.server.dao.exception.DataValidationException;
  41 +import org.thingsboard.server.dao.model.AssetEntity;
  42 +import org.thingsboard.server.dao.model.nosql.CustomerEntity;
  43 +import org.thingsboard.server.dao.model.nosql.TenantEntity;
  44 +import org.thingsboard.server.dao.relation.EntityRelationsQuery;
  45 +import org.thingsboard.server.dao.relation.EntitySearchDirection;
  46 +import org.thingsboard.server.dao.service.DataValidator;
  47 +import org.thingsboard.server.dao.service.PaginatedRemover;
  48 +import org.thingsboard.server.dao.tenant.TenantDao;
  49 +
  50 +import javax.annotation.Nullable;
  51 +import java.util.ArrayList;
  52 +import java.util.List;
  53 +import java.util.Optional;
  54 +import java.util.stream.Collectors;
  55 +
  56 +import static org.thingsboard.server.dao.DaoUtil.*;
  57 +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
  58 +import static org.thingsboard.server.dao.service.Validator.*;
  59 +
  60 +@Service
  61 +@Slf4j
  62 +public class BaseAssetService extends BaseEntityService implements AssetService {
  63 +
  64 + @Autowired
  65 + private AssetDao assetDao;
  66 +
  67 + @Autowired
  68 + private TenantDao tenantDao;
  69 +
  70 + @Autowired
  71 + private CustomerDao customerDao;
  72 +
  73 + @Override
  74 + public Asset findAssetById(AssetId assetId) {
  75 + log.trace("Executing findAssetById [{}]", assetId);
  76 + validateId(assetId, "Incorrect assetId " + assetId);
  77 + return assetDao.findById(assetId.getId());
  78 + }
  79 +
  80 + @Override
  81 + public ListenableFuture<Asset> findAssetByIdAsync(AssetId assetId) {
  82 + log.trace("Executing findAssetById [{}]", assetId);
  83 + validateId(assetId, "Incorrect assetId " + assetId);
  84 + return assetDao.findByIdAsync(assetId.getId());
  85 + }
  86 +
  87 + @Override
  88 + public Optional<Asset> findAssetByTenantIdAndName(TenantId tenantId, String name) {
  89 + log.trace("Executing findAssetByTenantIdAndName [{}][{}]", tenantId, name);
  90 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  91 + return assetDao.findAssetsByTenantIdAndName(tenantId.getId(), name);
  92 + }
  93 +
  94 + @Override
  95 + public Asset saveAsset(Asset asset) {
  96 + log.trace("Executing saveAsset [{}]", asset);
  97 + assetValidator.validate(asset);
  98 + return assetDao.save(asset);
  99 + }
  100 +
  101 + @Override
  102 + public Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId) {
  103 + Asset asset = findAssetById(assetId);
  104 + asset.setCustomerId(customerId);
  105 + return saveAsset(asset);
  106 + }
  107 +
  108 + @Override
  109 + public Asset unassignAssetFromCustomer(AssetId assetId) {
  110 + Asset asset = findAssetById(assetId);
  111 + asset.setCustomerId(null);
  112 + return saveAsset(asset);
  113 + }
  114 +
  115 + @Override
  116 + public void deleteAsset(AssetId assetId) {
  117 + log.trace("Executing deleteAsset [{}]", assetId);
  118 + validateId(assetId, "Incorrect assetId " + assetId);
  119 + deleteEntityRelations(assetId);
  120 + assetDao.removeById(assetId.getId());
  121 + }
  122 +
  123 + @Override
  124 + public TextPageData<Asset> findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink) {
  125 + log.trace("Executing findAssetsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
  126 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  127 + validatePageLink(pageLink, "Incorrect page link " + pageLink);
  128 + List<Asset> assets = assetDao.findAssetsByTenantId(tenantId.getId(), pageLink);
  129 + return new TextPageData<>(assets, pageLink);
  130 + }
  131 +
  132 + @Override
  133 + public ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List<AssetId> assetIds) {
  134 + log.trace("Executing findAssetsByTenantIdAndIdsAsync, tenantId [{}], assetIds [{}]", tenantId, assetIds);
  135 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  136 + validateIds(assetIds, "Incorrect assetIds " + assetIds);
  137 + return assetDao.findAssetsByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(assetIds));
  138 + }
  139 +
  140 + @Override
  141 + public void deleteAssetsByTenantId(TenantId tenantId) {
  142 + log.trace("Executing deleteAssetsByTenantId, tenantId [{}]", tenantId);
  143 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  144 + tenantAssetsRemover.removeEntitites(tenantId);
  145 + }
  146 +
  147 + @Override
  148 + public TextPageData<Asset> findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink) {
  149 + log.trace("Executing findAssetsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
  150 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  151 + validateId(customerId, "Incorrect customerId " + customerId);
  152 + validatePageLink(pageLink, "Incorrect page link " + pageLink);
  153 + List<Asset> assets = assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
  154 + return new TextPageData<Asset>(assets, pageLink);
  155 + }
  156 +
  157 + @Override
  158 + public ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<AssetId> assetIds) {
  159 + log.trace("Executing findAssetsByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], assetIds [{}]", tenantId, customerId, assetIds);
  160 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  161 + validateId(customerId, "Incorrect customerId " + customerId);
  162 + validateIds(assetIds, "Incorrect assetIds " + assetIds);
  163 + return assetDao.findAssetsByTenantIdCustomerIdAndIdsAsync(tenantId.getId(), customerId.getId(), toUUIDs(assetIds));
  164 + }
  165 +
  166 + @Override
  167 + public void unassignCustomerAssets(TenantId tenantId, CustomerId customerId) {
  168 + log.trace("Executing unassignCustomerAssets, tenantId [{}], customerId [{}]", tenantId, customerId);
  169 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  170 + validateId(customerId, "Incorrect customerId " + customerId);
  171 + new CustomerAssetsUnassigner(tenantId).removeEntitites(customerId);
  172 + }
  173 +
  174 + @Override
  175 + public ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query) {
  176 + ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery());
  177 + ListenableFuture<List<Asset>> assets = Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<Asset>>) relations1 -> {
  178 + EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
  179 + List<ListenableFuture<Asset>> futures = new ArrayList<>();
  180 + for (EntityRelation relation : relations1) {
  181 + EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
  182 + if (entityId.getEntityType() == EntityType.ASSET) {
  183 + futures.add(findAssetByIdAsync(new AssetId(entityId.getId())));
  184 + }
  185 + }
  186 + return Futures.successfulAsList(futures);
  187 + });
  188 +
  189 + assets = Futures.transform(assets, new Function<List<Asset>, List<Asset>>() {
  190 + @Nullable
  191 + @Override
  192 + public List<Asset> apply(@Nullable List<Asset> assetList) {
  193 + return assetList.stream().filter(asset -> query.getAssetTypes().contains(asset.getType())).collect(Collectors.toList());
  194 + }
  195 + });
  196 +
  197 + return assets;
  198 + }
  199 +
  200 + private DataValidator<Asset> assetValidator =
  201 + new DataValidator<Asset>() {
  202 +
  203 + @Override
  204 + protected void validateCreate(Asset asset) {
  205 + assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
  206 + d -> {
  207 + throw new DataValidationException("Asset with such name already exists!");
  208 + }
  209 + );
  210 + }
  211 +
  212 + @Override
  213 + protected void validateUpdate(Asset asset) {
  214 + assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
  215 + d -> {
  216 + if (!d.getId().equals(asset.getUuidId())) {
  217 + throw new DataValidationException("Asset with such name already exists!");
  218 + }
  219 + }
  220 + );
  221 + }
  222 +
  223 + @Override
  224 + protected void validateDataImpl(Asset asset) {
  225 + if (StringUtils.isEmpty(asset.getName())) {
  226 + throw new DataValidationException("Asset name should be specified!");
  227 + }
  228 + if (asset.getTenantId() == null) {
  229 + throw new DataValidationException("Asset should be assigned to tenant!");
  230 + } else {
  231 + Tenant tenant = tenantDao.findById(asset.getTenantId().getId());
  232 + if (tenant == null) {
  233 + throw new DataValidationException("Asset is referencing to non-existent tenant!");
  234 + }
  235 + }
  236 + if (asset.getCustomerId() == null) {
  237 + asset.setCustomerId(new CustomerId(NULL_UUID));
  238 + } else if (!asset.getCustomerId().getId().equals(NULL_UUID)) {
  239 + Customer customer = customerDao.findById(asset.getCustomerId().getId());
  240 + if (customer == null) {
  241 + throw new DataValidationException("Can't assign asset to non-existent customer!");
  242 + }
  243 + if (!customer.getTenantId().equals(asset.getTenantId().getId())) {
  244 + throw new DataValidationException("Can't assign asset to customer from different tenant!");
  245 + }
  246 + }
  247 + }
  248 + };
  249 +
  250 + private PaginatedRemover<TenantId, Asset> tenantAssetsRemover =
  251 + new PaginatedRemover<TenantId, Asset>() {
  252 +
  253 + @Override
  254 + protected List<Asset> findEntities(TenantId id, TextPageLink pageLink) {
  255 + return assetDao.findAssetsByTenantId(id.getId(), pageLink);
  256 + }
  257 +
  258 + @Override
  259 + protected void removeEntity(Asset entity) {
  260 + deleteAsset(new AssetId(entity.getId().getId()));
  261 + }
  262 + };
  263 +
  264 + class CustomerAssetsUnassigner extends PaginatedRemover<CustomerId, Asset> {
  265 +
  266 + private TenantId tenantId;
  267 +
  268 + CustomerAssetsUnassigner(TenantId tenantId) {
  269 + this.tenantId = tenantId;
  270 + }
  271 +
  272 + @Override
  273 + protected List<Asset> findEntities(CustomerId id, TextPageLink pageLink) {
  274 + return assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
  275 + }
  276 +
  277 + @Override
  278 + protected void removeEntity(Asset entity) {
  279 + unassignAssetFromCustomer(new AssetId(entity.getId().getId()));
  280 + }
  281 + }
  282 +}
... ...
... ... @@ -15,6 +15,13 @@
15 15 */
16 16 package org.thingsboard.server.dao.customer;
17 17
  18 +import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
  19 +import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_BY_TENANT_AND_TITLE_VIEW_NAME;
  20 +import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TITLE_PROPERTY;
  21 +import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TENANT_ID_PROPERTY;
  22 +
  23 +import java.util.Optional;
  24 +import com.datastax.driver.core.querybuilder.Select;
18 25 import lombok.extern.slf4j.Slf4j;
19 26 import org.springframework.stereotype.Component;
20 27 import org.thingsboard.server.common.data.Customer;
... ... @@ -53,4 +60,15 @@ public class CassandraCustomerDao extends CassandraAbstractSearchTextDao<Custome
53 60 return DaoUtil.convertDataList(customerEntities);
54 61 }
55 62
  63 + @Override
  64 + public Optional<Customer> findCustomersByTenantIdAndTitle(UUID tenantId, String title) {
  65 + Select select = select().from(CUSTOMER_BY_TENANT_AND_TITLE_VIEW_NAME);
  66 + Select.Where query = select.where();
  67 + query.and(eq(CUSTOMER_TENANT_ID_PROPERTY, tenantId));
  68 + query.and(eq(CUSTOMER_TITLE_PROPERTY, title));
  69 + CustomerEntity customerEntity = findOneByStatement(query);
  70 + Customer customer = DaoUtil.getData(customerEntity);
  71 + return Optional.ofNullable(customer);
  72 + }
  73 +
56 74 }
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.dao.customer;
17 17
  18 +import java.util.Optional;
18 19 import org.thingsboard.server.common.data.Customer;
19 20 import org.thingsboard.server.common.data.page.TextPageLink;
20 21 import org.thingsboard.server.dao.Dao;
... ... @@ -43,5 +44,14 @@ public interface CustomerDao extends Dao<Customer> {
43 44 * @return the list of customer objects
44 45 */
45 46 List<Customer> findCustomersByTenantId(UUID tenantId, TextPageLink pageLink);
  47 +
  48 + /**
  49 + * Find customers by tenantId and customer title.
  50 + *
  51 + * @param tenantId the tenantId
  52 + * @param title the customer title
  53 + * @return the optional customer object
  54 + */
  55 + Optional<Customer> findCustomersByTenantIdAndTitle(UUID tenantId, String title);
46 56
47 57 }
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.dao.customer;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 19 import org.thingsboard.server.common.data.Customer;
19 20 import org.thingsboard.server.common.data.id.CustomerId;
20 21 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -24,13 +25,17 @@ import org.thingsboard.server.common.data.page.TextPageLink;
24 25 public interface CustomerService {
25 26
26 27 Customer findCustomerById(CustomerId customerId);
27   -
  28 +
  29 + ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId);
  30 +
28 31 Customer saveCustomer(Customer customer);
29 32
30 33 void deleteCustomer(CustomerId customerId);
31   -
  34 +
  35 + Customer findOrCreatePublicCustomer(TenantId tenantId);
  36 +
32 37 TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink);
33 38
34 39 void deleteCustomersByTenantId(TenantId tenantId);
35   -
  40 +
36 41 }
... ...
... ... @@ -15,30 +15,42 @@
15 15 */
16 16 package org.thingsboard.server.dao.customer;
17 17
  18 +import static org.thingsboard.server.dao.service.Validator.validateId;
  19 +import java.io.IOException;
  20 +import java.util.List;
  21 +import java.util.Optional;
  22 +import com.fasterxml.jackson.databind.JsonNode;
  23 +import com.fasterxml.jackson.databind.ObjectMapper;
  24 +import com.google.common.base.Function;
  25 +import com.google.common.util.concurrent.Futures;
  26 +import com.google.common.util.concurrent.ListenableFuture;
18 27 import lombok.extern.slf4j.Slf4j;
19 28 import org.apache.commons.lang3.StringUtils;
20 29 import org.springframework.beans.factory.annotation.Autowired;
21 30 import org.springframework.stereotype.Service;
22 31 import org.thingsboard.server.common.data.Customer;
23 32 import org.thingsboard.server.common.data.Tenant;
  33 +import org.thingsboard.server.common.data.asset.Asset;
24 34 import org.thingsboard.server.common.data.id.CustomerId;
25 35 import org.thingsboard.server.common.data.id.TenantId;
26 36 import org.thingsboard.server.common.data.page.TextPageData;
27 37 import org.thingsboard.server.common.data.page.TextPageLink;
28 38 import org.thingsboard.server.dao.dashboard.DashboardService;
29 39 import org.thingsboard.server.dao.device.DeviceService;
  40 +import org.thingsboard.server.dao.entity.BaseEntityService;
30 41 import org.thingsboard.server.dao.exception.DataValidationException;
31 42 import org.thingsboard.server.dao.exception.IncorrectParameterException;
  43 +import org.thingsboard.server.dao.model.AssetEntity;
32 44 import org.thingsboard.server.dao.service.DataValidator;
33 45 import org.thingsboard.server.dao.service.PaginatedRemover;
34 46 import org.thingsboard.server.dao.service.Validator;
35 47 import org.thingsboard.server.dao.tenant.TenantDao;
36 48 import org.thingsboard.server.dao.user.UserService;
37   -
38   -import java.util.List;
39 49 @Service
40 50 @Slf4j
41   -public class CustomerServiceImpl implements CustomerService {
  51 +public class CustomerServiceImpl extends BaseEntityService implements CustomerService {
  52 +
  53 + private static final String PUBLIC_CUSTOMER_TITLE = "Public";
42 54
43 55 @Autowired
44 56 private CustomerDao customerDao;
... ... @@ -63,6 +75,13 @@ public class CustomerServiceImpl implements CustomerService {
63 75 }
64 76
65 77 @Override
  78 + public ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId) {
  79 + log.trace("Executing findCustomerByIdAsync [{}]", customerId);
  80 + validateId(customerId, "Incorrect customerId " + customerId);
  81 + return customerDao.findByIdAsync(customerId.getId());
  82 + }
  83 +
  84 + @Override
66 85 public Customer saveCustomer(Customer customer) {
67 86 log.trace("Executing saveCustomer [{}]", customer);
68 87 customerValidator.validate(customer);
... ... @@ -72,24 +91,45 @@ public class CustomerServiceImpl implements CustomerService {
72 91 @Override
73 92 public void deleteCustomer(CustomerId customerId) {
74 93 log.trace("Executing deleteCustomer [{}]", customerId);
75   - Validator.validateId(customerId, "Incorrect tenantId " + customerId);
  94 + Validator.validateId(customerId, "Incorrect customerId " + customerId);
76 95 Customer customer = findCustomerById(customerId);
77 96 if (customer == null) {
78 97 throw new IncorrectParameterException("Unable to delete non-existent customer.");
79 98 }
80 99 dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId);
81 100 deviceService.unassignCustomerDevices(customer.getTenantId(), customerId);
82   - userService.deleteCustomerUsers(customer.getTenantId(), customerId);
  101 + userService.deleteCustomerUsers(customer.getTenantId(), customerId);
  102 + deleteEntityRelations(customerId);
83 103 customerDao.removeById(customerId.getId());
84 104 }
85 105
86 106 @Override
  107 + public Customer findOrCreatePublicCustomer(TenantId tenantId) {
  108 + log.trace("Executing findOrCreatePublicCustomer, tenantId [{}]", tenantId);
  109 + Validator.validateId(tenantId, "Incorrect customerId " + tenantId);
  110 + Optional<Customer> publicCustomerOpt = customerDao.findCustomersByTenantIdAndTitle(tenantId.getId(), PUBLIC_CUSTOMER_TITLE);
  111 + if (publicCustomerOpt.isPresent()) {
  112 + return publicCustomerOpt.get();
  113 + } else {
  114 + Customer publicCustomer = new Customer();
  115 + publicCustomer.setTenantId(tenantId);
  116 + publicCustomer.setTitle(PUBLIC_CUSTOMER_TITLE);
  117 + try {
  118 + publicCustomer.setAdditionalInfo(new ObjectMapper().readValue("{ \"isPublic\": true }", JsonNode.class));
  119 + } catch (IOException e) {
  120 + throw new IncorrectParameterException("Unable to create public customer.", e);
  121 + }
  122 + return customerDao.save(publicCustomer);
  123 + }
  124 + }
  125 +
  126 + @Override
87 127 public TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink) {
88 128 log.trace("Executing findCustomersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
89 129 Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
90 130 Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink);
91 131 List<Customer> customers = customerDao.findCustomersByTenantId(tenantId.getId(), pageLink);
92   - return new TextPageData<Customer>(customers, pageLink);
  132 + return new TextPageData<>(customers, pageLink);
93 133 }
94 134
95 135 @Override
... ... @@ -101,11 +141,35 @@ public class CustomerServiceImpl implements CustomerService {
101 141
102 142 private DataValidator<Customer> customerValidator =
103 143 new DataValidator<Customer>() {
  144 +
  145 + @Override
  146 + protected void validateCreate(Customer customer) {
  147 + customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent(
  148 + c -> {
  149 + throw new DataValidationException("Customer with such title already exists!");
  150 + }
  151 + );
  152 + }
  153 +
  154 + @Override
  155 + protected void validateUpdate(Customer customer) {
  156 + customerDao.findCustomersByTenantIdAndTitle(customer.getTenantId().getId(), customer.getTitle()).ifPresent(
  157 + c -> {
  158 + if (!c.getId().equals(customer.getUuidId())) {
  159 + throw new DataValidationException("Customer with such title already exists!");
  160 + }
  161 + }
  162 + );
  163 + }
  164 +
104 165 @Override
105 166 protected void validateDataImpl(Customer customer) {
106 167 if (StringUtils.isEmpty(customer.getTitle())) {
107 168 throw new DataValidationException("Customer title should be specified!");
108 169 }
  170 + if (customer.getTitle().equals(PUBLIC_CUSTOMER_TITLE)) {
  171 + throw new DataValidationException("'Public' title for customer is system reserved!");
  172 + }
109 173 if (!StringUtils.isEmpty(customer.getEmail())) {
110 174 validateEmail(customer.getEmail());
111 175 }
... ...
... ... @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.id.TenantId;
29 29 import org.thingsboard.server.common.data.page.TextPageData;
30 30 import org.thingsboard.server.common.data.page.TextPageLink;
31 31 import org.thingsboard.server.dao.customer.CustomerDao;
  32 +import org.thingsboard.server.dao.entity.BaseEntityService;
32 33 import org.thingsboard.server.dao.exception.DataValidationException;
33 34 import org.thingsboard.server.dao.model.ModelConstants;
34 35 import org.thingsboard.server.dao.service.DataValidator;
... ... @@ -40,7 +41,7 @@ import java.util.List;
40 41
41 42 @Service
42 43 @Slf4j
43   -public class DashboardServiceImpl implements DashboardService {
  44 +public class DashboardServiceImpl extends BaseEntityService implements DashboardService {
44 45
45 46 @Autowired
46 47 private DashboardDao dashboardDao;
... ... @@ -86,6 +87,7 @@ public class DashboardServiceImpl implements DashboardService {
86 87 public void deleteDashboard(DashboardId dashboardId) {
87 88 log.trace("Executing deleteDashboard [{}]", dashboardId);
88 89 Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
  90 + deleteEntityRelations(dashboardId);
89 91 dashboardDao.removeById(dashboardId.getId());
90 92 }
91 93
... ...
... ... @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.page.TextPageLink;
32 32 import org.thingsboard.server.common.data.security.DeviceCredentials;
33 33 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
34 34 import org.thingsboard.server.dao.customer.CustomerDao;
  35 +import org.thingsboard.server.dao.entity.BaseEntityService;
35 36 import org.thingsboard.server.dao.exception.DataValidationException;
36 37 import org.thingsboard.server.dao.service.DataValidator;
37 38 import org.thingsboard.server.dao.service.PaginatedRemover;
... ... @@ -48,7 +49,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink;
48 49
49 50 @Service
50 51 @Slf4j
51   -public class DeviceServiceImpl implements DeviceService {
  52 +public class DeviceServiceImpl extends BaseEntityService implements DeviceService {
52 53
53 54 @Autowired
54 55 private DeviceDao deviceDao;
... ... @@ -125,6 +126,7 @@ public class DeviceServiceImpl implements DeviceService {
125 126 if (deviceCredentials != null) {
126 127 deviceCredentialsService.deleteDeviceCredentials(deviceCredentials);
127 128 }
  129 + deleteEntityRelations(deviceId);
128 130 deviceDao.removeById(deviceId.getId());
129 131 }
130 132
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.entity;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.thingsboard.server.common.data.id.EntityId;
  21 +import org.thingsboard.server.dao.relation.RelationService;
  22 +
  23 +/**
  24 + * Created by ashvayka on 04.05.17.
  25 + */
  26 +@Slf4j
  27 +public class BaseEntityService {
  28 +
  29 + @Autowired
  30 + protected RelationService relationService;
  31 +
  32 + protected void deleteEntityRelations(EntityId entityId) {
  33 + log.trace("Executing deleteEntityRelations [{}]", entityId);
  34 + relationService.deleteEntityRelations(entityId);
  35 + }
  36 +
  37 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model;
  17 +
  18 +import com.datastax.driver.core.utils.UUIDs;
  19 +import com.datastax.driver.mapping.annotations.*;
  20 +import com.fasterxml.jackson.databind.JsonNode;
  21 +import org.thingsboard.server.common.data.EntityType;
  22 +import org.thingsboard.server.common.data.alarm.Alarm;
  23 +import org.thingsboard.server.common.data.alarm.AlarmId;
  24 +import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  25 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  26 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  27 +import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.dao.model.type.AlarmSeverityCodec;
  29 +import org.thingsboard.server.dao.model.type.AlarmStatusCodec;
  30 +import org.thingsboard.server.dao.model.type.EntityTypeCodec;
  31 +import org.thingsboard.server.dao.model.type.JsonCodec;
  32 +
  33 +import java.util.UUID;
  34 +
  35 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  36 +
  37 +@Table(name = ALARM_COLUMN_FAMILY_NAME)
  38 +public final class AlarmEntity implements BaseEntity<Alarm> {
  39 +
  40 + @Transient
  41 + private static final long serialVersionUID = -1265181166886910152L;
  42 +
  43 + @ClusteringColumn(value = 1)
  44 + @Column(name = ID_PROPERTY)
  45 + private UUID id;
  46 +
  47 + @PartitionKey(value = 0)
  48 + @Column(name = ALARM_TENANT_ID_PROPERTY)
  49 + private UUID tenantId;
  50 +
  51 + @PartitionKey(value = 1)
  52 + @Column(name = ALARM_ORIGINATOR_ID_PROPERTY)
  53 + private UUID originatorId;
  54 +
  55 + @PartitionKey(value = 2)
  56 + @Column(name = ALARM_ORIGINATOR_TYPE_PROPERTY, codec = EntityTypeCodec.class)
  57 + private EntityType originatorType;
  58 +
  59 + @ClusteringColumn(value = 0)
  60 + @Column(name = ALARM_TYPE_PROPERTY)
  61 + private String type;
  62 +
  63 + @Column(name = ALARM_SEVERITY_PROPERTY, codec = AlarmSeverityCodec.class)
  64 + private AlarmSeverity severity;
  65 +
  66 + @Column(name = ALARM_STATUS_PROPERTY, codec = AlarmStatusCodec.class)
  67 + private AlarmStatus status;
  68 +
  69 + @Column(name = ALARM_START_TS_PROPERTY)
  70 + private Long startTs;
  71 +
  72 + @Column(name = ALARM_END_TS_PROPERTY)
  73 + private Long endTs;
  74 +
  75 + @Column(name = ALARM_ACK_TS_PROPERTY)
  76 + private Long ackTs;
  77 +
  78 + @Column(name = ALARM_CLEAR_TS_PROPERTY)
  79 + private Long clearTs;
  80 +
  81 + @Column(name = ALARM_DETAILS_PROPERTY, codec = JsonCodec.class)
  82 + private JsonNode details;
  83 +
  84 + @Column(name = ALARM_PROPAGATE_PROPERTY)
  85 + private Boolean propagate;
  86 +
  87 + public AlarmEntity() {
  88 + super();
  89 + }
  90 +
  91 + public AlarmEntity(Alarm alarm) {
  92 + if (alarm.getId() != null) {
  93 + this.id = alarm.getId().getId();
  94 + }
  95 + if (alarm.getTenantId() != null) {
  96 + this.tenantId = alarm.getTenantId().getId();
  97 + }
  98 + this.type = alarm.getType();
  99 + this.originatorId = alarm.getOriginator().getId();
  100 + this.originatorType = alarm.getOriginator().getEntityType();
  101 + this.type = alarm.getType();
  102 + this.severity = alarm.getSeverity();
  103 + this.status = alarm.getStatus();
  104 + this.propagate = alarm.isPropagate();
  105 + this.startTs = alarm.getStartTs();
  106 + this.endTs = alarm.getEndTs();
  107 + this.ackTs = alarm.getAckTs();
  108 + this.clearTs = alarm.getClearTs();
  109 + this.details = alarm.getDetails();
  110 + }
  111 +
  112 + public UUID getId() {
  113 + return id;
  114 + }
  115 +
  116 + public void setId(UUID id) {
  117 + this.id = id;
  118 + }
  119 +
  120 + public UUID getTenantId() {
  121 + return tenantId;
  122 + }
  123 +
  124 + public void setTenantId(UUID tenantId) {
  125 + this.tenantId = tenantId;
  126 + }
  127 +
  128 + public UUID getOriginatorId() {
  129 + return originatorId;
  130 + }
  131 +
  132 + public void setOriginatorId(UUID originatorId) {
  133 + this.originatorId = originatorId;
  134 + }
  135 +
  136 + public EntityType getOriginatorType() {
  137 + return originatorType;
  138 + }
  139 +
  140 + public void setOriginatorType(EntityType originatorType) {
  141 + this.originatorType = originatorType;
  142 + }
  143 +
  144 + public String getType() {
  145 + return type;
  146 + }
  147 +
  148 + public void setType(String type) {
  149 + this.type = type;
  150 + }
  151 +
  152 + public AlarmSeverity getSeverity() {
  153 + return severity;
  154 + }
  155 +
  156 + public void setSeverity(AlarmSeverity severity) {
  157 + this.severity = severity;
  158 + }
  159 +
  160 + public AlarmStatus getStatus() {
  161 + return status;
  162 + }
  163 +
  164 + public void setStatus(AlarmStatus status) {
  165 + this.status = status;
  166 + }
  167 +
  168 + public Long getStartTs() {
  169 + return startTs;
  170 + }
  171 +
  172 + public void setStartTs(Long startTs) {
  173 + this.startTs = startTs;
  174 + }
  175 +
  176 + public Long getEndTs() {
  177 + return endTs;
  178 + }
  179 +
  180 + public void setEndTs(Long endTs) {
  181 + this.endTs = endTs;
  182 + }
  183 +
  184 + public Long getAckTs() {
  185 + return ackTs;
  186 + }
  187 +
  188 + public void setAckTs(Long ackTs) {
  189 + this.ackTs = ackTs;
  190 + }
  191 +
  192 + public Long getClearTs() {
  193 + return clearTs;
  194 + }
  195 +
  196 + public void setClearTs(Long clearTs) {
  197 + this.clearTs = clearTs;
  198 + }
  199 +
  200 + public JsonNode getDetails() {
  201 + return details;
  202 + }
  203 +
  204 + public void setDetails(JsonNode details) {
  205 + this.details = details;
  206 + }
  207 +
  208 + public Boolean getPropagate() {
  209 + return propagate;
  210 + }
  211 +
  212 + public void setPropagate(Boolean propagate) {
  213 + this.propagate = propagate;
  214 + }
  215 +
  216 + @Override
  217 + public Alarm toData() {
  218 + Alarm alarm = new Alarm(new AlarmId(id));
  219 + alarm.setCreatedTime(UUIDs.unixTimestamp(id));
  220 + if (tenantId != null) {
  221 + alarm.setTenantId(new TenantId(tenantId));
  222 + }
  223 + alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, originatorId));
  224 + alarm.setType(type);
  225 + alarm.setSeverity(severity);
  226 + alarm.setStatus(status);
  227 + alarm.setPropagate(propagate);
  228 + alarm.setStartTs(startTs);
  229 + alarm.setEndTs(endTs);
  230 + alarm.setAckTs(ackTs);
  231 + alarm.setClearTs(clearTs);
  232 + alarm.setDetails(details);
  233 + return alarm;
  234 + }
  235 +
  236 +}
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model;
  17 +
  18 +import com.datastax.driver.core.utils.UUIDs;
  19 +import com.datastax.driver.mapping.annotations.Column;
  20 +import com.datastax.driver.mapping.annotations.PartitionKey;
  21 +import com.datastax.driver.mapping.annotations.Table;
  22 +import com.datastax.driver.mapping.annotations.Transient;
  23 +import com.fasterxml.jackson.databind.JsonNode;
  24 +import org.thingsboard.server.common.data.asset.Asset;
  25 +import org.thingsboard.server.common.data.id.AssetId;
  26 +import org.thingsboard.server.common.data.id.CustomerId;
  27 +import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.dao.model.type.JsonCodec;
  29 +
  30 +import java.util.UUID;
  31 +
  32 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  33 +
  34 +@Table(name = ASSET_COLUMN_FAMILY_NAME)
  35 +public final class AssetEntity implements SearchTextEntity<Asset> {
  36 +
  37 + @Transient
  38 + private static final long serialVersionUID = -1265181166886910152L;
  39 +
  40 + @PartitionKey(value = 0)
  41 + @Column(name = ID_PROPERTY)
  42 + private UUID id;
  43 +
  44 + @PartitionKey(value = 1)
  45 + @Column(name = ASSET_TENANT_ID_PROPERTY)
  46 + private UUID tenantId;
  47 +
  48 + @PartitionKey(value = 2)
  49 + @Column(name = ASSET_CUSTOMER_ID_PROPERTY)
  50 + private UUID customerId;
  51 +
  52 + @Column(name = ASSET_NAME_PROPERTY)
  53 + private String name;
  54 +
  55 + @Column(name = ASSET_TYPE_PROPERTY)
  56 + private String type;
  57 +
  58 + @Column(name = SEARCH_TEXT_PROPERTY)
  59 + private String searchText;
  60 +
  61 + @Column(name = ASSET_ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class)
  62 + private JsonNode additionalInfo;
  63 +
  64 + public AssetEntity() {
  65 + super();
  66 + }
  67 +
  68 + public AssetEntity(Asset asset) {
  69 + if (asset.getId() != null) {
  70 + this.id = asset.getId().getId();
  71 + }
  72 + if (asset.getTenantId() != null) {
  73 + this.tenantId = asset.getTenantId().getId();
  74 + }
  75 + if (asset.getCustomerId() != null) {
  76 + this.customerId = asset.getCustomerId().getId();
  77 + }
  78 + this.name = asset.getName();
  79 + this.type = asset.getType();
  80 + this.additionalInfo = asset.getAdditionalInfo();
  81 + }
  82 +
  83 + public UUID getId() {
  84 + return id;
  85 + }
  86 +
  87 + public void setId(UUID id) {
  88 + this.id = id;
  89 + }
  90 +
  91 + public UUID getTenantId() {
  92 + return tenantId;
  93 + }
  94 +
  95 + public void setTenantId(UUID tenantId) {
  96 + this.tenantId = tenantId;
  97 + }
  98 +
  99 + public UUID getCustomerId() {
  100 + return customerId;
  101 + }
  102 +
  103 + public void setCustomerId(UUID customerId) {
  104 + this.customerId = customerId;
  105 + }
  106 +
  107 + public String getName() {
  108 + return name;
  109 + }
  110 +
  111 + public void setName(String name) {
  112 + this.name = name;
  113 + }
  114 +
  115 + public String getType() {
  116 + return type;
  117 + }
  118 +
  119 + public void setType(String type) {
  120 + this.type = type;
  121 + }
  122 +
  123 + public JsonNode getAdditionalInfo() {
  124 + return additionalInfo;
  125 + }
  126 +
  127 + public void setAdditionalInfo(JsonNode additionalInfo) {
  128 + this.additionalInfo = additionalInfo;
  129 + }
  130 +
  131 + @Override
  132 + public String getSearchTextSource() {
  133 + return name;
  134 + }
  135 +
  136 + @Override
  137 + public void setSearchText(String searchText) {
  138 + this.searchText = searchText;
  139 + }
  140 +
  141 + public String getSearchText() {
  142 + return searchText;
  143 + }
  144 +
  145 + @Override
  146 + public int hashCode() {
  147 + final int prime = 31;
  148 + int result = 1;
  149 + result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode());
  150 + result = prime * result + ((customerId == null) ? 0 : customerId.hashCode());
  151 + result = prime * result + ((id == null) ? 0 : id.hashCode());
  152 + result = prime * result + ((name == null) ? 0 : name.hashCode());
  153 + result = prime * result + ((type == null) ? 0 : type.hashCode());
  154 + result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
  155 + return result;
  156 + }
  157 +
  158 + @Override
  159 + public boolean equals(Object obj) {
  160 + if (this == obj)
  161 + return true;
  162 + if (obj == null)
  163 + return false;
  164 + if (getClass() != obj.getClass())
  165 + return false;
  166 + AssetEntity other = (AssetEntity) obj;
  167 + if (additionalInfo == null) {
  168 + if (other.additionalInfo != null)
  169 + return false;
  170 + } else if (!additionalInfo.equals(other.additionalInfo))
  171 + return false;
  172 + if (customerId == null) {
  173 + if (other.customerId != null)
  174 + return false;
  175 + } else if (!customerId.equals(other.customerId))
  176 + return false;
  177 + if (id == null) {
  178 + if (other.id != null)
  179 + return false;
  180 + } else if (!id.equals(other.id))
  181 + return false;
  182 + if (name == null) {
  183 + if (other.name != null)
  184 + return false;
  185 + } else if (!name.equals(other.name))
  186 + return false;
  187 + if (type == null) {
  188 + if (other.type != null)
  189 + return false;
  190 + } else if (!type.equals(other.type))
  191 + return false;
  192 + if (tenantId == null) {
  193 + if (other.tenantId != null)
  194 + return false;
  195 + } else if (!tenantId.equals(other.tenantId))
  196 + return false;
  197 + return true;
  198 + }
  199 +
  200 + @Override
  201 + public String toString() {
  202 + StringBuilder builder = new StringBuilder();
  203 + builder.append("AssetEntity [id=");
  204 + builder.append(id);
  205 + builder.append(", tenantId=");
  206 + builder.append(tenantId);
  207 + builder.append(", customerId=");
  208 + builder.append(customerId);
  209 + builder.append(", name=");
  210 + builder.append(name);
  211 + builder.append(", type=");
  212 + builder.append(type);
  213 + builder.append(", additionalInfo=");
  214 + builder.append(additionalInfo);
  215 + builder.append("]");
  216 + return builder.toString();
  217 + }
  218 +
  219 + @Override
  220 + public Asset toData() {
  221 + Asset asset = new Asset(new AssetId(id));
  222 + asset.setCreatedTime(UUIDs.unixTimestamp(id));
  223 + if (tenantId != null) {
  224 + asset.setTenantId(new TenantId(tenantId));
  225 + }
  226 + if (customerId != null) {
  227 + asset.setCustomerId(new CustomerId(customerId));
  228 + }
  229 + asset.setName(name);
  230 + asset.setType(type);
  231 + asset.setAdditionalInfo(additionalInfo);
  232 + return asset;
  233 + }
  234 +
  235 +}
\ No newline at end of file
... ...
... ... @@ -112,6 +112,7 @@ public class ModelConstants {
112 112 public static final String CUSTOMER_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
113 113
114 114 public static final String CUSTOMER_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "customer_by_tenant_and_search_text";
  115 + public static final String CUSTOMER_BY_TENANT_AND_TITLE_VIEW_NAME = "customer_by_tenant_and_title";
115 116
116 117 /**
117 118 * Cassandra device constants.
... ... @@ -126,6 +127,51 @@ public class ModelConstants {
126 127 public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text";
127 128 public static final String DEVICE_BY_TENANT_AND_NAME_VIEW_NAME = "device_by_tenant_and_name";
128 129
  130 + /**
  131 + * Cassandra asset constants.
  132 + */
  133 + public static final String ASSET_COLUMN_FAMILY_NAME = "asset";
  134 + public static final String ASSET_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
  135 + public static final String ASSET_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
  136 + public static final String ASSET_NAME_PROPERTY = "name";
  137 + public static final String ASSET_TYPE_PROPERTY = "type";
  138 + public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
  139 +
  140 + public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text";
  141 + public static final String ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_and_search_text";
  142 + public static final String ASSET_BY_TENANT_AND_NAME_VIEW_NAME = "asset_by_tenant_and_name";
  143 +
  144 + /**
  145 + * Cassandra alarm constants.
  146 + */
  147 + public static final String ALARM_COLUMN_FAMILY_NAME = "alarm";
  148 + public static final String ALARM_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
  149 + public static final String ALARM_TYPE_PROPERTY = "type";
  150 + public static final String ALARM_DETAILS_PROPERTY = "details";
  151 + public static final String ALARM_ORIGINATOR_ID_PROPERTY = "originator_id";
  152 + public static final String ALARM_ORIGINATOR_TYPE_PROPERTY = "originator_type";
  153 + public static final String ALARM_SEVERITY_PROPERTY = "severity";
  154 + public static final String ALARM_STATUS_PROPERTY = "status";
  155 + public static final String ALARM_START_TS_PROPERTY = "start_ts";
  156 + public static final String ALARM_END_TS_PROPERTY = "end_ts";
  157 + public static final String ALARM_ACK_TS_PROPERTY = "ack_ts";
  158 + public static final String ALARM_CLEAR_TS_PROPERTY = "clear_ts";
  159 + public static final String ALARM_PROPAGATE_PROPERTY = "propagate";
  160 +
  161 + public static final String ALARM_BY_ID_VIEW_NAME = "alarm_by_id";
  162 +
  163 + /**
  164 + * Cassandra entity relation constants.
  165 + */
  166 + public static final String RELATION_COLUMN_FAMILY_NAME = "relation";
  167 + public static final String RELATION_FROM_ID_PROPERTY = "from_id";
  168 + public static final String RELATION_FROM_TYPE_PROPERTY = "from_type";
  169 + public static final String RELATION_TO_ID_PROPERTY = "to_id";
  170 + public static final String RELATION_TO_TYPE_PROPERTY = "to_type";
  171 + public static final String RELATION_TYPE_PROPERTY = "relation_type";
  172 +
  173 + public static final String RELATION_REVERSE_VIEW_NAME = "reverse_relation";
  174 +
129 175
130 176 /**
131 177 * Cassandra device_credentials constants.
... ...
... ... @@ -99,23 +99,7 @@ public class EventEntity implements BaseEntity<Event> {
99 99 Event event = new Event(new EventId(id));
100 100 event.setCreatedTime(UUIDs.unixTimestamp(id));
101 101 event.setTenantId(new TenantId(tenantId));
102   - switch (entityType) {
103   - case TENANT:
104   - event.setEntityId(new TenantId(entityId));
105   - break;
106   - case DEVICE:
107   - event.setEntityId(new DeviceId(entityId));
108   - break;
109   - case CUSTOMER:
110   - event.setEntityId(new CustomerId(entityId));
111   - break;
112   - case RULE:
113   - event.setEntityId(new RuleId(entityId));
114   - break;
115   - case PLUGIN:
116   - event.setEntityId(new PluginId(entityId));
117   - break;
118   - }
  102 + event.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId));
119 103 event.setBody(body);
120 104 event.setType(eventType);
121 105 event.setUid(eventUid);
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.type;
  17 +
  18 +import com.datastax.driver.extras.codecs.enums.EnumNameCodec;
  19 +import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  20 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  21 +import org.thingsboard.server.dao.alarm.AlarmService;
  22 +
  23 +public class AlarmSeverityCodec extends EnumNameCodec<AlarmSeverity> {
  24 +
  25 + public AlarmSeverityCodec() {
  26 + super(AlarmSeverity.class);
  27 + }
  28 +
  29 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.type;
  17 +
  18 +import com.datastax.driver.extras.codecs.enums.EnumNameCodec;
  19 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  20 +
  21 +public class AlarmStatusCodec extends EnumNameCodec<AlarmStatus> {
  22 +
  23 + public AlarmStatusCodec() {
  24 + super(AlarmStatus.class);
  25 + }
  26 +
  27 +}
... ...
... ... @@ -15,11 +15,14 @@
15 15 */
16 16 package org.thingsboard.server.dao.plugin;
17 17
  18 +import com.google.common.util.concurrent.Futures;
  19 +import com.google.common.util.concurrent.ListenableFuture;
18 20 import lombok.extern.slf4j.Slf4j;
19 21 import org.apache.commons.lang3.StringUtils;
20 22 import org.springframework.beans.factory.annotation.Autowired;
21 23 import org.springframework.stereotype.Service;
22 24 import org.thingsboard.server.common.data.id.PluginId;
  25 +import org.thingsboard.server.common.data.id.RuleId;
23 26 import org.thingsboard.server.common.data.id.TenantId;
24 27 import org.thingsboard.server.common.data.page.TextPageData;
25 28 import org.thingsboard.server.common.data.page.TextPageLink;
... ... @@ -29,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
29 32 import org.thingsboard.server.common.data.plugin.PluginMetaData;
30 33 import org.thingsboard.server.common.data.rule.RuleMetaData;
31 34 import org.thingsboard.server.dao.component.ComponentDescriptorService;
  35 +import org.thingsboard.server.dao.entity.BaseEntityService;
32 36 import org.thingsboard.server.dao.exception.DataValidationException;
33 37 import org.thingsboard.server.dao.exception.DatabaseException;
34 38 import org.thingsboard.server.dao.exception.IncorrectParameterException;
... ... @@ -43,9 +47,10 @@ import java.util.List;
43 47 import java.util.UUID;
44 48 import java.util.stream.Collectors;
45 49
  50 +import static org.thingsboard.server.dao.service.Validator.validateId;
46 51 @Service
47 52 @Slf4j
48   -public class BasePluginService implements PluginService {
  53 +public class BasePluginService extends BaseEntityService implements PluginService {
49 54
50 55 //TODO: move to a better place.
51 56 public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
... ... @@ -103,6 +108,12 @@ public class BasePluginService implements PluginService {
103 108 }
104 109
105 110 @Override
  111 + public ListenableFuture<PluginMetaData> findPluginByIdAsync(PluginId pluginId) {
  112 + validateId(pluginId, "Incorrect plugin id for search plugin request.");
  113 + return pluginDao.findByIdAsync(pluginId.getId());
  114 + }
  115 +
  116 + @Override
106 117 public PluginMetaData findPluginByApiToken(String apiToken) {
107 118 Validator.validateString(apiToken, "Incorrect plugin apiToken for search request.");
108 119 return pluginDao.findByApiToken(apiToken);
... ... @@ -196,6 +207,7 @@ public class BasePluginService implements PluginService {
196 207 @Override
197 208 public void deletePluginById(PluginId pluginId) {
198 209 Validator.validateId(pluginId, "Incorrect plugin id for delete request.");
  210 + deleteEntityRelations(pluginId);
199 211 checkRulesAndDelete(pluginId.getId());
200 212 }
201 213
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.dao.plugin;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 19 import org.thingsboard.server.common.data.id.PluginId;
19 20 import org.thingsboard.server.common.data.id.TenantId;
20 21 import org.thingsboard.server.common.data.page.TextPageData;
... ... @@ -29,6 +30,8 @@ public interface PluginService {
29 30
30 31 PluginMetaData findPluginById(PluginId pluginId);
31 32
  33 + ListenableFuture<PluginMetaData> findPluginByIdAsync(PluginId pluginId);
  34 +
32 35 PluginMetaData findPluginByApiToken(String apiToken);
33 36
34 37 TextPageData<PluginMetaData> findSystemPlugins(TextPageLink pageLink);
... ...