Commit 0661de2392cb10035ced56be675eb97e94c41337
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.
... | ... | @@ -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 | ... | ... |
application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
0 → 100644
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 | ... | ... |
application/src/main/java/org/thingsboard/server/service/security/auth/rest/PublicLoginRequest.java
0 → 100644
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 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/security/model/UserPrincipal.java
0 → 100644
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); | ... | ... |
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"); | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdDeserializer.java
0 → 100644
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 | } | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java
0 → 100644
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); | ... | ... |