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,6 +37,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | ||
37 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 37 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
38 | import org.thingsboard.server.common.transport.auth.DeviceAuthService; | 38 | import org.thingsboard.server.common.transport.auth.DeviceAuthService; |
39 | import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; | 39 | import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; |
40 | +import org.thingsboard.server.dao.asset.AssetService; | ||
40 | import org.thingsboard.server.dao.attributes.AttributesService; | 41 | import org.thingsboard.server.dao.attributes.AttributesService; |
41 | import org.thingsboard.server.dao.customer.CustomerService; | 42 | import org.thingsboard.server.dao.customer.CustomerService; |
42 | import org.thingsboard.server.dao.device.DeviceService; | 43 | import org.thingsboard.server.dao.device.DeviceService; |
@@ -81,6 +82,9 @@ public class ActorSystemContext { | @@ -81,6 +82,9 @@ public class ActorSystemContext { | ||
81 | @Getter private DeviceService deviceService; | 82 | @Getter private DeviceService deviceService; |
82 | 83 | ||
83 | @Autowired | 84 | @Autowired |
85 | + @Getter private AssetService assetService; | ||
86 | + | ||
87 | + @Autowired | ||
84 | @Getter private TenantService tenantService; | 88 | @Getter private TenantService tenantService; |
85 | 89 | ||
86 | @Autowired | 90 | @Autowired |
@@ -51,13 +51,7 @@ import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestBody; | @@ -51,13 +51,7 @@ import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestBody; | ||
51 | import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; | 51 | import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; |
52 | import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; | 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 | import java.util.concurrent.ExecutionException; | 55 | import java.util.concurrent.ExecutionException; |
62 | import java.util.concurrent.TimeoutException; | 56 | import java.util.concurrent.TimeoutException; |
63 | import java.util.function.Consumer; | 57 | import java.util.function.Consumer; |
@@ -205,25 +199,21 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | @@ -205,25 +199,21 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso | ||
205 | 199 | ||
206 | void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) { | 200 | void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) { |
207 | refreshAttributes(msg); | 201 | refreshAttributes(msg); |
208 | - Set<AttributeKey> keys = msg.getDeletedKeys(); | ||
209 | if (attributeSubscriptions.size() > 0) { | 202 | if (attributeSubscriptions.size() > 0) { |
210 | ToDeviceMsg notification = null; | 203 | ToDeviceMsg notification = null; |
211 | if (msg.isDeleted()) { | 204 | if (msg.isDeleted()) { |
212 | - List<AttributeKey> sharedKeys = keys.stream() | 205 | + List<AttributeKey> sharedKeys = msg.getDeletedKeys().stream() |
213 | .filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope())) | 206 | .filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope())) |
214 | .collect(Collectors.toList()); | 207 | .collect(Collectors.toList()); |
215 | notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromDeleted(sharedKeys)); | 208 | notification = new AttributesUpdateNotification(BasicAttributeKVMsg.fromDeleted(sharedKeys)); |
216 | } else { | 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 | if (notification != null) { | 219 | if (notification != null) { |
@@ -24,17 +24,19 @@ import com.google.common.util.concurrent.FutureCallback; | @@ -24,17 +24,19 @@ import com.google.common.util.concurrent.FutureCallback; | ||
24 | import com.google.common.util.concurrent.Futures; | 24 | import com.google.common.util.concurrent.Futures; |
25 | import com.google.common.util.concurrent.ListenableFuture; | 25 | import com.google.common.util.concurrent.ListenableFuture; |
26 | import lombok.extern.slf4j.Slf4j; | 26 | import lombok.extern.slf4j.Slf4j; |
27 | -import org.thingsboard.server.common.data.DataConstants; | 27 | +import org.thingsboard.server.common.data.Customer; |
28 | import org.thingsboard.server.common.data.Device; | 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 | import org.thingsboard.server.common.data.id.TenantId; | 32 | import org.thingsboard.server.common.data.id.TenantId; |
33 | import org.thingsboard.server.common.data.kv.AttributeKey; | 33 | import org.thingsboard.server.common.data.kv.AttributeKey; |
34 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 34 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
35 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 35 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
36 | import org.thingsboard.server.common.data.kv.TsKvQuery; | 36 | import org.thingsboard.server.common.data.kv.TsKvQuery; |
37 | import org.thingsboard.server.common.data.page.TextPageLink; | 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 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 40 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
39 | import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; | 41 | import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; |
40 | import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; | 42 | import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; |
@@ -89,103 +91,107 @@ public final class PluginProcessingContext implements PluginContext { | @@ -89,103 +91,107 @@ public final class PluginProcessingContext implements PluginContext { | ||
89 | } | 91 | } |
90 | 92 | ||
91 | @Override | 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 | Futures.addCallback(rsListFuture, getListCallback(callback, v -> { | 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 | return null; | 101 | return null; |
98 | }), executor); | 102 | }), executor); |
99 | })); | 103 | })); |
100 | } | 104 | } |
101 | 105 | ||
102 | @Override | 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 | Futures.addCallback(future, getCallback(callback, v -> null), executor); | 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 | @Override | 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 | Futures.addCallback(future, getCallback(callback, v -> v), executor); | 121 | Futures.addCallback(future, getCallback(callback, v -> v), executor); |
116 | })); | 122 | })); |
117 | } | 123 | } |
118 | 124 | ||
119 | @Override | 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 | Futures.addCallback(future, getCallback(callback, v -> v), executor); | 129 | Futures.addCallback(future, getCallback(callback, v -> v), executor); |
124 | })); | 130 | })); |
125 | } | 131 | } |
126 | 132 | ||
127 | @Override | 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 | Futures.addCallback(future, getCallback(callback, v -> v), executor); | 137 | Futures.addCallback(future, getCallback(callback, v -> v), executor); |
132 | })); | 138 | })); |
133 | } | 139 | } |
134 | 140 | ||
135 | @Override | 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 | List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); | 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 | convertFuturesAndAddCallback(callback, futures); | 146 | convertFuturesAndAddCallback(callback, futures); |
141 | })); | 147 | })); |
142 | } | 148 | } |
143 | 149 | ||
144 | @Override | 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 | List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); | 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 | convertFuturesAndAddCallback(callback, futures); | 155 | convertFuturesAndAddCallback(callback, futures); |
150 | })); | 156 | })); |
151 | } | 157 | } |
152 | 158 | ||
153 | @Override | 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 | Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor); | 163 | Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor); |
158 | })); | 164 | })); |
159 | } | 165 | } |
160 | 166 | ||
161 | @Override | 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 | Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor); | 171 | Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor); |
166 | })); | 172 | })); |
167 | } | 173 | } |
168 | 174 | ||
169 | @Override | 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 | Futures.addCallback(future, getCallback(callback, v -> v), executor); | 179 | Futures.addCallback(future, getCallback(callback, v -> v), executor); |
174 | })); | 180 | })); |
175 | } | 181 | } |
176 | 182 | ||
177 | @Override | 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 | Futures.addCallback(future, getCallback(callback, pluginCtx.tsService::convertResultSetToTsKvEntryList), executor); | 187 | Futures.addCallback(future, getCallback(callback, pluginCtx.tsService::convertResultSetToTsKvEntryList), executor); |
182 | })); | 188 | })); |
183 | } | 189 | } |
184 | 190 | ||
185 | @Override | 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 | Futures.addCallback(rsListFuture, getListCallback(callback, rsList -> | 195 | Futures.addCallback(rsListFuture, getListCallback(callback, rsList -> |
190 | { | 196 | { |
191 | List<TsKvEntry> result = new ArrayList<>(); | 197 | List<TsKvEntry> result = new ArrayList<>(); |
@@ -268,24 +274,101 @@ public final class PluginProcessingContext implements PluginContext { | @@ -268,24 +274,101 @@ public final class PluginProcessingContext implements PluginContext { | ||
268 | validate(deviceId, new ValidationCallback(callback, ctx -> callback.onSuccess(ctx, null))); | 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 | if (securityCtx.isPresent()) { | 278 | if (securityCtx.isPresent()) { |
273 | final PluginApiCallSecurityContext ctx = securityCtx.get(); | 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 | } else { | 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 | } else { | 372 | } else { |
290 | callback.onSuccess(this, Boolean.FALSE); | 373 | callback.onSuccess(this, Boolean.FALSE); |
291 | } | 374 | } |
@@ -295,8 +378,8 @@ public final class PluginProcessingContext implements PluginContext { | @@ -295,8 +378,8 @@ public final class PluginProcessingContext implements PluginContext { | ||
295 | } | 378 | } |
296 | 379 | ||
297 | @Override | 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 | @Override | 385 | @Override |
@@ -25,8 +25,13 @@ import org.thingsboard.server.common.data.id.TenantId; | @@ -25,8 +25,13 @@ import org.thingsboard.server.common.data.id.TenantId; | ||
25 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 25 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
26 | import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; | 26 | import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; |
27 | import org.thingsboard.server.common.data.id.PluginId; | 27 | import org.thingsboard.server.common.data.id.PluginId; |
28 | +import org.thingsboard.server.dao.asset.AssetService; | ||
28 | import org.thingsboard.server.dao.attributes.AttributesService; | 29 | import org.thingsboard.server.dao.attributes.AttributesService; |
30 | +import org.thingsboard.server.dao.customer.CustomerService; | ||
29 | import org.thingsboard.server.dao.device.DeviceService; | 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 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 35 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
31 | import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; | 36 | import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; |
32 | import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; | 37 | import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; |
@@ -46,7 +51,12 @@ public final class SharedPluginProcessingContext { | @@ -46,7 +51,12 @@ public final class SharedPluginProcessingContext { | ||
46 | final ActorRef currentActor; | 51 | final ActorRef currentActor; |
47 | final ActorSystemContext systemContext; | 52 | final ActorSystemContext systemContext; |
48 | final PluginWebSocketMsgEndpoint msgEndpoint; | 53 | final PluginWebSocketMsgEndpoint msgEndpoint; |
54 | + final AssetService assetService; | ||
49 | final DeviceService deviceService; | 55 | final DeviceService deviceService; |
56 | + final RuleService ruleService; | ||
57 | + final PluginService pluginService; | ||
58 | + final CustomerService customerService; | ||
59 | + final TenantService tenantService; | ||
50 | final TimeseriesService tsService; | 60 | final TimeseriesService tsService; |
51 | final AttributesService attributesService; | 61 | final AttributesService attributesService; |
52 | final ClusterRpcService rpcService; | 62 | final ClusterRpcService rpcService; |
@@ -65,9 +75,14 @@ public final class SharedPluginProcessingContext { | @@ -65,9 +75,14 @@ public final class SharedPluginProcessingContext { | ||
65 | this.msgEndpoint = sysContext.getWsMsgEndpoint(); | 75 | this.msgEndpoint = sysContext.getWsMsgEndpoint(); |
66 | this.tsService = sysContext.getTsService(); | 76 | this.tsService = sysContext.getTsService(); |
67 | this.attributesService = sysContext.getAttributesService(); | 77 | this.attributesService = sysContext.getAttributesService(); |
78 | + this.assetService = sysContext.getAssetService(); | ||
68 | this.deviceService = sysContext.getDeviceService(); | 79 | this.deviceService = sysContext.getDeviceService(); |
69 | this.rpcService = sysContext.getRpcService(); | 80 | this.rpcService = sysContext.getRpcService(); |
70 | this.routingService = sysContext.getRoutingService(); | 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 | public PluginId getPluginId() { | 88 | public PluginId getPluginId() { |
@@ -89,7 +104,7 @@ public final class SharedPluginProcessingContext { | @@ -89,7 +104,7 @@ public final class SharedPluginProcessingContext { | ||
89 | } | 104 | } |
90 | 105 | ||
91 | private <T> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) { | 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 | if (instance.isPresent()) { | 108 | if (instance.isPresent()) { |
94 | log.trace("[{}] Forwarding msg {} to remote device actor!", pluginId, msg); | 109 | log.trace("[{}] Forwarding msg {} to remote device actor!", pluginId, msg); |
95 | rpcFunction.accept(instance.get(), msg); | 110 | rpcFunction.accept(instance.get(), msg); |
@@ -38,7 +38,7 @@ public class ValidationCallback implements PluginCallback<Boolean> { | @@ -38,7 +38,7 @@ public class ValidationCallback implements PluginCallback<Boolean> { | ||
38 | if (value) { | 38 | if (value) { |
39 | action.accept(ctx); | 39 | action.accept(ctx); |
40 | } else { | 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,7 +230,7 @@ public class DefaultActorService implements ActorService { | ||
230 | @Override | 230 | @Override |
231 | public void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId) { | 231 | public void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId) { |
232 | DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId); | 232 | DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId); |
233 | - Optional<ServerAddress> address = actorContext.getRoutingService().resolve(deviceId); | 233 | + Optional<ServerAddress> address = actorContext.getRoutingService().resolveById(deviceId); |
234 | if (address.isPresent()) { | 234 | if (address.isPresent()) { |
235 | rpcService.tell(address.get(), msg); | 235 | rpcService.tell(address.get(), msg); |
236 | } else { | 236 | } else { |
@@ -116,7 +116,7 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { | @@ -116,7 +116,7 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { | ||
116 | @Override | 116 | @Override |
117 | public void processClusterEvent(ActorContext context, ClusterEventMsg msg) { | 117 | public void processClusterEvent(ActorContext context, ClusterEventMsg msg) { |
118 | if (pendingMap.size() > 0 || subscribedToAttributeUpdates || subscribedToRpcCommands) { | 118 | if (pendingMap.size() > 0 || subscribedToAttributeUpdates || subscribedToRpcCommands) { |
119 | - Optional<ServerAddress> newTargetServer = systemContext.getRoutingService().resolve(getDeviceId()); | 119 | + Optional<ServerAddress> newTargetServer = systemContext.getRoutingService().resolveById(getDeviceId()); |
120 | if (!newTargetServer.equals(currentTargetServer)) { | 120 | if (!newTargetServer.equals(currentTargetServer)) { |
121 | firstMsg = true; | 121 | firstMsg = true; |
122 | currentTargetServer = newTargetServer; | 122 | currentTargetServer = newTargetServer; |
@@ -81,13 +81,13 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP | @@ -81,13 +81,13 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP | ||
81 | } | 81 | } |
82 | 82 | ||
83 | protected Optional<ServerAddress> forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward) { | 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 | forwardToAppActor(ctx, toForward, address); | 85 | forwardToAppActor(ctx, toForward, address); |
86 | return address; | 86 | return address; |
87 | } | 87 | } |
88 | 88 | ||
89 | protected Optional<ServerAddress> forwardToAppActorIfAdressChanged(ActorContext ctx, ToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) { | 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 | if (!newAddress.equals(oldAddress)) { | 91 | if (!newAddress.equals(oldAddress)) { |
92 | if (newAddress.isPresent()) { | 92 | if (newAddress.isPresent()) { |
93 | systemContext.getRpcService().tell(newAddress.get(), | 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,7 +18,9 @@ package org.thingsboard.server.config; | ||
18 | import com.fasterxml.jackson.databind.ObjectMapper; | 18 | import com.fasterxml.jackson.databind.ObjectMapper; |
19 | import org.springframework.beans.factory.annotation.Autowired; | 19 | import org.springframework.beans.factory.annotation.Autowired; |
20 | import org.springframework.beans.factory.annotation.Qualifier; | 20 | import org.springframework.beans.factory.annotation.Qualifier; |
21 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
21 | import org.springframework.boot.autoconfigure.security.SecurityProperties; | 22 | import org.springframework.boot.autoconfigure.security.SecurityProperties; |
23 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
22 | import org.springframework.context.annotation.Bean; | 24 | import org.springframework.context.annotation.Bean; |
23 | import org.springframework.context.annotation.Configuration; | 25 | import org.springframework.context.annotation.Configuration; |
24 | import org.springframework.core.annotation.Order; | 26 | import org.springframework.core.annotation.Order; |
@@ -34,11 +36,15 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand | @@ -34,11 +36,15 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand | ||
34 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; | 36 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; |
35 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | 37 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
36 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; | 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 | import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; | 42 | import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; |
38 | import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider; | 43 | import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider; |
39 | import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter; | 44 | import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter; |
40 | import org.thingsboard.server.service.security.auth.jwt.*; | 45 | import org.thingsboard.server.service.security.auth.jwt.*; |
41 | import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; | 46 | import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; |
47 | +import org.thingsboard.server.service.security.auth.rest.RestPublicLoginProcessingFilter; | ||
42 | 48 | ||
43 | import java.util.ArrayList; | 49 | import java.util.ArrayList; |
44 | import java.util.Arrays; | 50 | import java.util.Arrays; |
@@ -56,6 +62,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -56,6 +62,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
56 | public static final String WEBJARS_ENTRY_POINT = "/webjars/**"; | 62 | public static final String WEBJARS_ENTRY_POINT = "/webjars/**"; |
57 | public static final String DEVICE_API_ENTRY_POINT = "/api/v1/**"; | 63 | public static final String DEVICE_API_ENTRY_POINT = "/api/v1/**"; |
58 | public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login"; | 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 | public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; | 66 | public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; |
60 | public static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/static/**", "/api/noauth/**", "/webjars/**"}; | 67 | public static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/static/**", "/api/noauth/**", "/webjars/**"}; |
61 | public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; | 68 | public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; |
@@ -88,9 +95,17 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -88,9 +95,17 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
88 | } | 95 | } |
89 | 96 | ||
90 | @Bean | 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 | protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception { | 105 | protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception { |
92 | List<String> pathsToSkip = new ArrayList(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS)); | 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 | SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT); | 109 | SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT); |
95 | JwtTokenAuthenticationProcessingFilter filter | 110 | JwtTokenAuthenticationProcessingFilter filter |
96 | = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher); | 111 | = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher); |
@@ -136,6 +151,8 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -136,6 +151,8 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
136 | protected void configure(HttpSecurity http) throws Exception { | 151 | protected void configure(HttpSecurity http) throws Exception { |
137 | http.headers().cacheControl().disable().frameOptions().disable() | 152 | http.headers().cacheControl().disable().frameOptions().disable() |
138 | .and() | 153 | .and() |
154 | + .cors() | ||
155 | + .and() | ||
139 | .csrf().disable() | 156 | .csrf().disable() |
140 | .exceptionHandling() | 157 | .exceptionHandling() |
141 | .and() | 158 | .and() |
@@ -146,6 +163,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -146,6 +163,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
146 | .antMatchers(WEBJARS_ENTRY_POINT).permitAll() // Webjars | 163 | .antMatchers(WEBJARS_ENTRY_POINT).permitAll() // Webjars |
147 | .antMatchers(DEVICE_API_ENTRY_POINT).permitAll() // Device HTTP Transport API | 164 | .antMatchers(DEVICE_API_ENTRY_POINT).permitAll() // Device HTTP Transport API |
148 | .antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point | 165 | .antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point |
166 | + .antMatchers(PUBLIC_LOGIN_ENTRY_POINT).permitAll() // Public login end-point | ||
149 | .antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point | 167 | .antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point |
150 | .antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points | 168 | .antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points |
151 | .and() | 169 | .and() |
@@ -156,8 +174,22 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -156,8 +174,22 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
156 | .exceptionHandling().accessDeniedHandler(restAccessDeniedHandler) | 174 | .exceptionHandling().accessDeniedHandler(restAccessDeniedHandler) |
157 | .and() | 175 | .and() |
158 | .addFilterBefore(buildRestLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class) | 176 | .addFilterBefore(buildRestLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class) |
177 | + .addFilterBefore(buildRestPublicLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class) | ||
159 | .addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class) | 178 | .addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class) |
160 | .addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class) | 179 | .addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class) |
161 | .addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class); | 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,6 +36,7 @@ import org.thingsboard.server.exception.ThingsboardException; | ||
36 | import org.thingsboard.server.service.mail.MailService; | 36 | import org.thingsboard.server.service.mail.MailService; |
37 | import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; | 37 | import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; |
38 | import org.thingsboard.server.service.security.model.SecurityUser; | 38 | import org.thingsboard.server.service.security.model.SecurityUser; |
39 | +import org.thingsboard.server.service.security.model.UserPrincipal; | ||
39 | import org.thingsboard.server.service.security.model.token.JwtToken; | 40 | import org.thingsboard.server.service.security.model.token.JwtToken; |
40 | import org.thingsboard.server.service.security.model.token.JwtTokenFactory; | 41 | import org.thingsboard.server.service.security.model.token.JwtTokenFactory; |
41 | 42 | ||
@@ -167,7 +168,8 @@ public class AuthController extends BaseController { | @@ -167,7 +168,8 @@ public class AuthController extends BaseController { | ||
167 | String encodedPassword = passwordEncoder.encode(password); | 168 | String encodedPassword = passwordEncoder.encode(password); |
168 | UserCredentials credentials = userService.activateUserCredentials(activateToken, encodedPassword); | 169 | UserCredentials credentials = userService.activateUserCredentials(activateToken, encodedPassword); |
169 | User user = userService.findUserById(credentials.getUserId()); | 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 | String baseUrl = constructBaseUrl(request); | 173 | String baseUrl = constructBaseUrl(request); |
172 | String loginUrl = String.format("%s/login", baseUrl); | 174 | String loginUrl = String.format("%s/login", baseUrl); |
173 | String email = user.getEmail(); | 175 | String email = user.getEmail(); |
@@ -201,7 +203,8 @@ public class AuthController extends BaseController { | @@ -201,7 +203,8 @@ public class AuthController extends BaseController { | ||
201 | userCredentials.setResetToken(null); | 203 | userCredentials.setResetToken(null); |
202 | userCredentials = userService.saveUserCredentials(userCredentials); | 204 | userCredentials = userService.saveUserCredentials(userCredentials); |
203 | User user = userService.findUserById(userCredentials.getUserId()); | 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 | String baseUrl = constructBaseUrl(request); | 208 | String baseUrl = constructBaseUrl(request); |
206 | String loginUrl = String.format("%s/login", baseUrl); | 209 | String loginUrl = String.format("%s/login", baseUrl); |
207 | String email = user.getEmail(); | 210 | String email = user.getEmail(); |
@@ -22,10 +22,8 @@ import org.springframework.security.core.Authentication; | @@ -22,10 +22,8 @@ import org.springframework.security.core.Authentication; | ||
22 | import org.springframework.security.core.context.SecurityContextHolder; | 22 | import org.springframework.security.core.context.SecurityContextHolder; |
23 | import org.springframework.web.bind.annotation.ExceptionHandler; | 23 | import org.springframework.web.bind.annotation.ExceptionHandler; |
24 | import org.thingsboard.server.actors.service.ActorService; | 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 | import org.thingsboard.server.common.data.id.*; | 27 | import org.thingsboard.server.common.data.id.*; |
30 | import org.thingsboard.server.common.data.page.TextPageLink; | 28 | import org.thingsboard.server.common.data.page.TextPageLink; |
31 | import org.thingsboard.server.common.data.page.TimePageLink; | 29 | import org.thingsboard.server.common.data.page.TimePageLink; |
@@ -36,6 +34,7 @@ import org.thingsboard.server.common.data.rule.RuleMetaData; | @@ -36,6 +34,7 @@ import org.thingsboard.server.common.data.rule.RuleMetaData; | ||
36 | import org.thingsboard.server.common.data.security.Authority; | 34 | import org.thingsboard.server.common.data.security.Authority; |
37 | import org.thingsboard.server.common.data.widget.WidgetType; | 35 | import org.thingsboard.server.common.data.widget.WidgetType; |
38 | import org.thingsboard.server.common.data.widget.WidgetsBundle; | 36 | import org.thingsboard.server.common.data.widget.WidgetsBundle; |
37 | +import org.thingsboard.server.dao.asset.AssetService; | ||
39 | import org.thingsboard.server.dao.customer.CustomerService; | 38 | import org.thingsboard.server.dao.customer.CustomerService; |
40 | import org.thingsboard.server.dao.dashboard.DashboardService; | 39 | import org.thingsboard.server.dao.dashboard.DashboardService; |
41 | import org.thingsboard.server.dao.device.DeviceCredentialsService; | 40 | import org.thingsboard.server.dao.device.DeviceCredentialsService; |
@@ -44,6 +43,7 @@ import org.thingsboard.server.dao.exception.DataValidationException; | @@ -44,6 +43,7 @@ import org.thingsboard.server.dao.exception.DataValidationException; | ||
44 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 43 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
45 | import org.thingsboard.server.dao.model.ModelConstants; | 44 | import org.thingsboard.server.dao.model.ModelConstants; |
46 | import org.thingsboard.server.dao.plugin.PluginService; | 45 | import org.thingsboard.server.dao.plugin.PluginService; |
46 | +import org.thingsboard.server.dao.relation.RelationService; | ||
47 | import org.thingsboard.server.dao.rule.RuleService; | 47 | import org.thingsboard.server.dao.rule.RuleService; |
48 | import org.thingsboard.server.dao.user.UserService; | 48 | import org.thingsboard.server.dao.user.UserService; |
49 | import org.thingsboard.server.dao.widget.WidgetTypeService; | 49 | import org.thingsboard.server.dao.widget.WidgetTypeService; |
@@ -79,6 +79,9 @@ public abstract class BaseController { | @@ -79,6 +79,9 @@ public abstract class BaseController { | ||
79 | protected DeviceService deviceService; | 79 | protected DeviceService deviceService; |
80 | 80 | ||
81 | @Autowired | 81 | @Autowired |
82 | + protected AssetService assetService; | ||
83 | + | ||
84 | + @Autowired | ||
82 | protected DeviceCredentialsService deviceCredentialsService; | 85 | protected DeviceCredentialsService deviceCredentialsService; |
83 | 86 | ||
84 | @Autowired | 87 | @Autowired |
@@ -102,6 +105,9 @@ public abstract class BaseController { | @@ -102,6 +105,9 @@ public abstract class BaseController { | ||
102 | @Autowired | 105 | @Autowired |
103 | protected ActorService actorService; | 106 | protected ActorService actorService; |
104 | 107 | ||
108 | + @Autowired | ||
109 | + protected RelationService relationService; | ||
110 | + | ||
105 | 111 | ||
106 | @ExceptionHandler(ThingsboardException.class) | 112 | @ExceptionHandler(ThingsboardException.class) |
107 | public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { | 113 | public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { |
@@ -251,6 +257,43 @@ public abstract class BaseController { | @@ -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 | Device checkDeviceId(DeviceId deviceId) throws ThingsboardException { | 297 | Device checkDeviceId(DeviceId deviceId) throws ThingsboardException { |
255 | try { | 298 | try { |
256 | validateId(deviceId, "Incorrect deviceId " + deviceId); | 299 | validateId(deviceId, "Incorrect deviceId " + deviceId); |
@@ -270,6 +313,25 @@ public abstract class BaseController { | @@ -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 | WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException { | 335 | WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException { |
274 | try { | 336 | try { |
275 | validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); | 337 | validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); |
@@ -385,6 +447,16 @@ public abstract class BaseController { | @@ -385,6 +447,16 @@ public abstract class BaseController { | ||
385 | return plugin; | 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 | protected RuleMetaData checkRule(RuleMetaData rule) throws ThingsboardException { | 460 | protected RuleMetaData checkRule(RuleMetaData rule) throws ThingsboardException { |
389 | checkNotNull(rule); | 461 | checkNotNull(rule); |
390 | SecurityUser authUser = getCurrentUser(); | 462 | SecurityUser authUser = getCurrentUser(); |
@@ -410,7 +482,8 @@ public abstract class BaseController { | @@ -410,7 +482,8 @@ public abstract class BaseController { | ||
410 | if (request.getHeader("x-forwarded-port") != null) { | 482 | if (request.getHeader("x-forwarded-port") != null) { |
411 | try { | 483 | try { |
412 | serverPort = request.getIntHeader("x-forwarded-port"); | 484 | serverPort = request.getIntHeader("x-forwarded-port"); |
413 | - } catch (NumberFormatException e) {} | 485 | + } catch (NumberFormatException e) { |
486 | + } | ||
414 | } | 487 | } |
415 | 488 | ||
416 | String baseUrl = String.format("%s://%s:%d", | 489 | String baseUrl = String.format("%s://%s:%d", |
@@ -15,6 +15,9 @@ | @@ -15,6 +15,9 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.controller; | 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 | import org.springframework.http.HttpStatus; | 21 | import org.springframework.http.HttpStatus; |
19 | import org.springframework.security.access.prepost.PreAuthorize; | 22 | import org.springframework.security.access.prepost.PreAuthorize; |
20 | import org.springframework.web.bind.annotation.*; | 23 | import org.springframework.web.bind.annotation.*; |
@@ -43,6 +46,28 @@ public class CustomerController extends BaseController { | @@ -43,6 +46,28 @@ public class CustomerController extends BaseController { | ||
43 | } | 46 | } |
44 | 47 | ||
45 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 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 | @RequestMapping(value = "/customer/{customerId}/title", method = RequestMethod.GET, produces = "application/text") | 71 | @RequestMapping(value = "/customer/{customerId}/title", method = RequestMethod.GET, produces = "application/text") |
47 | @ResponseBody | 72 | @ResponseBody |
48 | public String getCustomerTitleById(@PathVariable("customerId") String strCustomerId) throws ThingsboardException { | 73 | public String getCustomerTitleById(@PathVariable("customerId") String strCustomerId) throws ThingsboardException { |
@@ -18,6 +18,7 @@ package org.thingsboard.server.controller; | @@ -18,6 +18,7 @@ package org.thingsboard.server.controller; | ||
18 | import org.springframework.http.HttpStatus; | 18 | import org.springframework.http.HttpStatus; |
19 | import org.springframework.security.access.prepost.PreAuthorize; | 19 | import org.springframework.security.access.prepost.PreAuthorize; |
20 | import org.springframework.web.bind.annotation.*; | 20 | import org.springframework.web.bind.annotation.*; |
21 | +import org.thingsboard.server.common.data.Customer; | ||
21 | import org.thingsboard.server.common.data.Dashboard; | 22 | import org.thingsboard.server.common.data.Dashboard; |
22 | import org.thingsboard.server.common.data.DashboardInfo; | 23 | import org.thingsboard.server.common.data.DashboardInfo; |
23 | import org.thingsboard.server.common.data.id.CustomerId; | 24 | import org.thingsboard.server.common.data.id.CustomerId; |
@@ -117,6 +118,21 @@ public class DashboardController extends BaseController { | @@ -117,6 +118,21 @@ public class DashboardController extends BaseController { | ||
117 | } | 118 | } |
118 | 119 | ||
119 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") | 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 | @RequestMapping(value = "/tenant/dashboards", params = { "limit" }, method = RequestMethod.GET) | 136 | @RequestMapping(value = "/tenant/dashboards", params = { "limit" }, method = RequestMethod.GET) |
121 | @ResponseBody | 137 | @ResponseBody |
122 | public TextPageData<DashboardInfo> getTenantDashboards( | 138 | public TextPageData<DashboardInfo> getTenantDashboards( |
@@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; | @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; | ||
19 | import org.springframework.http.HttpStatus; | 19 | import org.springframework.http.HttpStatus; |
20 | import org.springframework.security.access.prepost.PreAuthorize; | 20 | import org.springframework.security.access.prepost.PreAuthorize; |
21 | import org.springframework.web.bind.annotation.*; | 21 | import org.springframework.web.bind.annotation.*; |
22 | +import org.thingsboard.server.common.data.Customer; | ||
22 | import org.thingsboard.server.common.data.Device; | 23 | import org.thingsboard.server.common.data.Device; |
23 | import org.thingsboard.server.common.data.id.CustomerId; | 24 | import org.thingsboard.server.common.data.id.CustomerId; |
24 | import org.thingsboard.server.common.data.id.DeviceId; | 25 | import org.thingsboard.server.common.data.id.DeviceId; |
@@ -114,6 +115,21 @@ public class DeviceController extends BaseController { | @@ -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 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 133 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
118 | @RequestMapping(value = "/device/{deviceId}/credentials", method = RequestMethod.GET) | 134 | @RequestMapping(value = "/device/{deviceId}/credentials", method = RequestMethod.GET) |
119 | @ResponseBody | 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,7 +18,6 @@ package org.thingsboard.server.controller; | ||
18 | import org.springframework.beans.factory.annotation.Autowired; | 18 | import org.springframework.beans.factory.annotation.Autowired; |
19 | import org.springframework.security.access.prepost.PreAuthorize; | 19 | import org.springframework.security.access.prepost.PreAuthorize; |
20 | import org.springframework.web.bind.annotation.*; | 20 | import org.springframework.web.bind.annotation.*; |
21 | -import org.thingsboard.server.common.data.EntityType; | ||
22 | import org.thingsboard.server.common.data.Event; | 21 | import org.thingsboard.server.common.data.Event; |
23 | import org.thingsboard.server.common.data.id.*; | 22 | import org.thingsboard.server.common.data.id.*; |
24 | import org.thingsboard.server.common.data.page.TimePageData; | 23 | import org.thingsboard.server.common.data.page.TimePageData; |
@@ -59,7 +58,7 @@ public class EventController extends BaseController { | @@ -59,7 +58,7 @@ public class EventController extends BaseController { | ||
59 | ThingsboardErrorCode.PERMISSION_DENIED); | 58 | ThingsboardErrorCode.PERMISSION_DENIED); |
60 | } | 59 | } |
61 | TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); | 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 | } catch (Exception e) { | 62 | } catch (Exception e) { |
64 | throw handleException(e); | 63 | throw handleException(e); |
65 | } | 64 | } |
@@ -88,29 +87,10 @@ public class EventController extends BaseController { | @@ -88,29 +87,10 @@ public class EventController extends BaseController { | ||
88 | ThingsboardErrorCode.PERMISSION_DENIED); | 87 | ThingsboardErrorCode.PERMISSION_DENIED); |
89 | } | 88 | } |
90 | TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); | 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 | } catch (Exception e) { | 91 | } catch (Exception e) { |
93 | throw handleException(e); | 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,11 +15,13 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.cluster.routing; | 16 | package org.thingsboard.server.service.cluster.routing; |
17 | 17 | ||
18 | +import org.thingsboard.server.common.data.id.EntityId; | ||
18 | import org.thingsboard.server.common.data.id.UUIDBased; | 19 | import org.thingsboard.server.common.data.id.UUIDBased; |
19 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 20 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
20 | import org.thingsboard.server.service.cluster.discovery.ServerInstance; | 21 | import org.thingsboard.server.service.cluster.discovery.ServerInstance; |
21 | 22 | ||
22 | import java.util.Optional; | 23 | import java.util.Optional; |
24 | +import java.util.UUID; | ||
23 | 25 | ||
24 | /** | 26 | /** |
25 | * @author Andrew Shvayka | 27 | * @author Andrew Shvayka |
@@ -28,6 +30,8 @@ public interface ClusterRoutingService { | @@ -28,6 +30,8 @@ public interface ClusterRoutingService { | ||
28 | 30 | ||
29 | ServerAddress getCurrentServer(); | 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,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired; | ||
22 | import org.springframework.beans.factory.annotation.Value; | 22 | import org.springframework.beans.factory.annotation.Value; |
23 | import org.springframework.stereotype.Service; | 23 | import org.springframework.stereotype.Service; |
24 | import org.springframework.util.Assert; | 24 | import org.springframework.util.Assert; |
25 | +import org.thingsboard.server.common.data.id.EntityId; | ||
25 | import org.thingsboard.server.common.data.id.UUIDBased; | 26 | import org.thingsboard.server.common.data.id.UUIDBased; |
26 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 27 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
27 | import org.thingsboard.server.service.cluster.discovery.DiscoveryService; | 28 | import org.thingsboard.server.service.cluster.discovery.DiscoveryService; |
@@ -31,6 +32,7 @@ import org.thingsboard.server.utils.MiscUtils; | @@ -31,6 +32,7 @@ import org.thingsboard.server.utils.MiscUtils; | ||
31 | 32 | ||
32 | import javax.annotation.PostConstruct; | 33 | import javax.annotation.PostConstruct; |
33 | import java.util.Optional; | 34 | import java.util.Optional; |
35 | +import java.util.UUID; | ||
34 | import java.util.concurrent.ConcurrentNavigableMap; | 36 | import java.util.concurrent.ConcurrentNavigableMap; |
35 | import java.util.concurrent.ConcurrentSkipListMap; | 37 | import java.util.concurrent.ConcurrentSkipListMap; |
36 | 38 | ||
@@ -77,13 +79,18 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D | @@ -77,13 +79,18 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D | ||
77 | } | 79 | } |
78 | 80 | ||
79 | @Override | 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 | if (circle.isEmpty()) { | 89 | if (circle.isEmpty()) { |
83 | return Optional.empty(); | 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 | if (!circle.containsKey(hash)) { | 94 | if (!circle.containsKey(hash)) { |
88 | ConcurrentNavigableMap<Long, ServerInstance> tailMap = | 95 | ConcurrentNavigableMap<Long, ServerInstance> tailMap = |
89 | circle.tailMap(hash); | 96 | circle.tailMap(hash); |
@@ -16,32 +16,40 @@ | @@ -16,32 +16,40 @@ | ||
16 | package org.thingsboard.server.service.security.auth.jwt; | 16 | package org.thingsboard.server.service.security.auth.jwt; |
17 | 17 | ||
18 | import org.springframework.beans.factory.annotation.Autowired; | 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 | import org.springframework.security.core.Authentication; | 20 | import org.springframework.security.core.Authentication; |
24 | import org.springframework.security.core.AuthenticationException; | 21 | import org.springframework.security.core.AuthenticationException; |
25 | import org.springframework.security.core.userdetails.UsernameNotFoundException; | 22 | import org.springframework.security.core.userdetails.UsernameNotFoundException; |
26 | import org.springframework.stereotype.Component; | 23 | import org.springframework.stereotype.Component; |
27 | import org.springframework.util.Assert; | 24 | import org.springframework.util.Assert; |
25 | +import org.thingsboard.server.common.data.Customer; | ||
28 | import org.thingsboard.server.common.data.User; | 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 | import org.thingsboard.server.common.data.security.UserCredentials; | 31 | import org.thingsboard.server.common.data.security.UserCredentials; |
32 | +import org.thingsboard.server.dao.customer.CustomerService; | ||
30 | import org.thingsboard.server.dao.user.UserService; | 33 | import org.thingsboard.server.dao.user.UserService; |
31 | import org.thingsboard.server.service.security.auth.RefreshAuthenticationToken; | 34 | import org.thingsboard.server.service.security.auth.RefreshAuthenticationToken; |
32 | import org.thingsboard.server.service.security.model.SecurityUser; | 35 | import org.thingsboard.server.service.security.model.SecurityUser; |
36 | +import org.thingsboard.server.service.security.model.UserPrincipal; | ||
33 | import org.thingsboard.server.service.security.model.token.JwtTokenFactory; | 37 | import org.thingsboard.server.service.security.model.token.JwtTokenFactory; |
34 | import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; | 38 | import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; |
35 | 39 | ||
40 | +import java.util.UUID; | ||
41 | + | ||
36 | @Component | 42 | @Component |
37 | public class RefreshTokenAuthenticationProvider implements AuthenticationProvider { | 43 | public class RefreshTokenAuthenticationProvider implements AuthenticationProvider { |
38 | 44 | ||
39 | private final JwtTokenFactory tokenFactory; | 45 | private final JwtTokenFactory tokenFactory; |
40 | private final UserService userService; | 46 | private final UserService userService; |
47 | + private final CustomerService customerService; | ||
41 | 48 | ||
42 | @Autowired | 49 | @Autowired |
43 | - public RefreshTokenAuthenticationProvider(final UserService userService, final JwtTokenFactory tokenFactory) { | 50 | + public RefreshTokenAuthenticationProvider(final UserService userService, final CustomerService customerService, final JwtTokenFactory tokenFactory) { |
44 | this.userService = userService; | 51 | this.userService = userService; |
52 | + this.customerService = customerService; | ||
45 | this.tokenFactory = tokenFactory; | 53 | this.tokenFactory = tokenFactory; |
46 | } | 54 | } |
47 | 55 | ||
@@ -50,8 +58,18 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide | @@ -50,8 +58,18 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide | ||
50 | Assert.notNull(authentication, "No authentication data provided"); | 58 | Assert.notNull(authentication, "No authentication data provided"); |
51 | RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); | 59 | RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); |
52 | SecurityUser unsafeUser = tokenFactory.parseRefreshToken(rawAccessToken); | 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 | if (user == null) { | 73 | if (user == null) { |
56 | throw new UsernameNotFoundException("User not found by refresh token"); | 74 | throw new UsernameNotFoundException("User not found by refresh token"); |
57 | } | 75 | } |
@@ -67,9 +85,44 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide | @@ -67,9 +85,44 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide | ||
67 | 85 | ||
68 | if (user.getAuthority() == null) throw new InsufficientAuthenticationException("User has no authority assigned"); | 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 | @Override | 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,20 +23,31 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
23 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | 23 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
24 | import org.springframework.stereotype.Component; | 24 | import org.springframework.stereotype.Component; |
25 | import org.springframework.util.Assert; | 25 | import org.springframework.util.Assert; |
26 | +import org.thingsboard.server.common.data.Customer; | ||
26 | import org.thingsboard.server.common.data.User; | 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 | import org.thingsboard.server.common.data.security.UserCredentials; | 32 | import org.thingsboard.server.common.data.security.UserCredentials; |
33 | +import org.thingsboard.server.dao.customer.CustomerService; | ||
28 | import org.thingsboard.server.dao.user.UserService; | 34 | import org.thingsboard.server.dao.user.UserService; |
29 | import org.thingsboard.server.service.security.model.SecurityUser; | 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 | @Component | 40 | @Component |
32 | public class RestAuthenticationProvider implements AuthenticationProvider { | 41 | public class RestAuthenticationProvider implements AuthenticationProvider { |
33 | 42 | ||
34 | private final BCryptPasswordEncoder encoder; | 43 | private final BCryptPasswordEncoder encoder; |
35 | private final UserService userService; | 44 | private final UserService userService; |
45 | + private final CustomerService customerService; | ||
36 | 46 | ||
37 | @Autowired | 47 | @Autowired |
38 | - public RestAuthenticationProvider(final UserService userService, final BCryptPasswordEncoder encoder) { | 48 | + public RestAuthenticationProvider(final UserService userService, final CustomerService customerService, final BCryptPasswordEncoder encoder) { |
39 | this.userService = userService; | 49 | this.userService = userService; |
50 | + this.customerService = customerService; | ||
40 | this.encoder = encoder; | 51 | this.encoder = encoder; |
41 | } | 52 | } |
42 | 53 | ||
@@ -44,9 +55,23 @@ public class RestAuthenticationProvider implements AuthenticationProvider { | @@ -44,9 +55,23 @@ public class RestAuthenticationProvider implements AuthenticationProvider { | ||
44 | public Authentication authenticate(Authentication authentication) throws AuthenticationException { | 55 | public Authentication authenticate(Authentication authentication) throws AuthenticationException { |
45 | Assert.notNull(authentication, "No authentication data provided"); | 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 | User user = userService.findUserByEmail(username); | 75 | User user = userService.findUserByEmail(username); |
51 | if (user == null) { | 76 | if (user == null) { |
52 | throw new UsernameNotFoundException("User not found: " + username); | 77 | throw new UsernameNotFoundException("User not found: " + username); |
@@ -67,7 +92,38 @@ public class RestAuthenticationProvider implements AuthenticationProvider { | @@ -67,7 +92,38 @@ public class RestAuthenticationProvider implements AuthenticationProvider { | ||
67 | 92 | ||
68 | if (user.getAuthority() == null) throw new InsufficientAuthenticationException("User has no authority assigned"); | 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 | return new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities()); | 128 | return new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities()); |
73 | } | 129 | } |
@@ -29,6 +29,7 @@ import org.springframework.security.web.authentication.AbstractAuthenticationPro | @@ -29,6 +29,7 @@ import org.springframework.security.web.authentication.AbstractAuthenticationPro | ||
29 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; | 29 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; |
30 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; | 30 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; |
31 | import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; | 31 | import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; |
32 | +import org.thingsboard.server.service.security.model.UserPrincipal; | ||
32 | 33 | ||
33 | import javax.servlet.FilterChain; | 34 | import javax.servlet.FilterChain; |
34 | import javax.servlet.ServletException; | 35 | import javax.servlet.ServletException; |
@@ -73,7 +74,9 @@ public class RestLoginProcessingFilter extends AbstractAuthenticationProcessingF | @@ -73,7 +74,9 @@ public class RestLoginProcessingFilter extends AbstractAuthenticationProcessingF | ||
73 | throw new AuthenticationServiceException("Username or Password not provided"); | 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 | return this.getAuthenticationManager().authenticate(token); | 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,6 +30,7 @@ public class SecurityUser extends User { | ||
30 | 30 | ||
31 | private Collection<GrantedAuthority> authorities; | 31 | private Collection<GrantedAuthority> authorities; |
32 | private boolean enabled; | 32 | private boolean enabled; |
33 | + private UserPrincipal userPrincipal; | ||
33 | 34 | ||
34 | public SecurityUser() { | 35 | public SecurityUser() { |
35 | super(); | 36 | super(); |
@@ -39,9 +40,10 @@ public class SecurityUser extends User { | @@ -39,9 +40,10 @@ public class SecurityUser extends User { | ||
39 | super(id); | 40 | super(id); |
40 | } | 41 | } |
41 | 42 | ||
42 | - public SecurityUser(User user, boolean enabled) { | 43 | + public SecurityUser(User user, boolean enabled, UserPrincipal userPrincipal) { |
43 | super(user); | 44 | super(user); |
44 | this.enabled = enabled; | 45 | this.enabled = enabled; |
46 | + this.userPrincipal = userPrincipal; | ||
45 | } | 47 | } |
46 | 48 | ||
47 | public Collection<? extends GrantedAuthority> getAuthorities() { | 49 | public Collection<? extends GrantedAuthority> getAuthorities() { |
@@ -57,8 +59,16 @@ public class SecurityUser extends User { | @@ -57,8 +59,16 @@ public class SecurityUser extends User { | ||
57 | return enabled; | 59 | return enabled; |
58 | } | 60 | } |
59 | 61 | ||
62 | + public UserPrincipal getUserPrincipal() { | ||
63 | + return userPrincipal; | ||
64 | + } | ||
65 | + | ||
60 | public void setEnabled(boolean enabled) { | 66 | public void setEnabled(boolean enabled) { |
61 | this.enabled = enabled; | 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,6 +29,7 @@ import org.thingsboard.server.common.data.id.UserId; | ||
29 | import org.thingsboard.server.common.data.security.Authority; | 29 | import org.thingsboard.server.common.data.security.Authority; |
30 | import org.thingsboard.server.config.JwtSettings; | 30 | import org.thingsboard.server.config.JwtSettings; |
31 | import org.thingsboard.server.service.security.model.SecurityUser; | 31 | import org.thingsboard.server.service.security.model.SecurityUser; |
32 | +import org.thingsboard.server.service.security.model.UserPrincipal; | ||
32 | 33 | ||
33 | import java.util.Arrays; | 34 | import java.util.Arrays; |
34 | import java.util.List; | 35 | import java.util.List; |
@@ -43,6 +44,7 @@ public class JwtTokenFactory { | @@ -43,6 +44,7 @@ public class JwtTokenFactory { | ||
43 | private static final String FIRST_NAME = "firstName"; | 44 | private static final String FIRST_NAME = "firstName"; |
44 | private static final String LAST_NAME = "lastName"; | 45 | private static final String LAST_NAME = "lastName"; |
45 | private static final String ENABLED = "enabled"; | 46 | private static final String ENABLED = "enabled"; |
47 | + private static final String IS_PUBLIC = "isPublic"; | ||
46 | private static final String TENANT_ID = "tenantId"; | 48 | private static final String TENANT_ID = "tenantId"; |
47 | private static final String CUSTOMER_ID = "customerId"; | 49 | private static final String CUSTOMER_ID = "customerId"; |
48 | 50 | ||
@@ -63,12 +65,15 @@ public class JwtTokenFactory { | @@ -63,12 +65,15 @@ public class JwtTokenFactory { | ||
63 | if (securityUser.getAuthority() == null) | 65 | if (securityUser.getAuthority() == null) |
64 | throw new IllegalArgumentException("User doesn't have any privileges"); | 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 | claims.put(SCOPES, securityUser.getAuthorities().stream().map(s -> s.getAuthority()).collect(Collectors.toList())); | 71 | claims.put(SCOPES, securityUser.getAuthorities().stream().map(s -> s.getAuthority()).collect(Collectors.toList())); |
68 | claims.put(USER_ID, securityUser.getId().getId().toString()); | 72 | claims.put(USER_ID, securityUser.getId().getId().toString()); |
69 | claims.put(FIRST_NAME, securityUser.getFirstName()); | 73 | claims.put(FIRST_NAME, securityUser.getFirstName()); |
70 | claims.put(LAST_NAME, securityUser.getLastName()); | 74 | claims.put(LAST_NAME, securityUser.getLastName()); |
71 | claims.put(ENABLED, securityUser.isEnabled()); | 75 | claims.put(ENABLED, securityUser.isEnabled()); |
76 | + claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID); | ||
72 | if (securityUser.getTenantId() != null) { | 77 | if (securityUser.getTenantId() != null) { |
73 | claims.put(TENANT_ID, securityUser.getTenantId().getId().toString()); | 78 | claims.put(TENANT_ID, securityUser.getTenantId().getId().toString()); |
74 | } | 79 | } |
@@ -104,6 +109,9 @@ public class JwtTokenFactory { | @@ -104,6 +109,9 @@ public class JwtTokenFactory { | ||
104 | securityUser.setFirstName(claims.get(FIRST_NAME, String.class)); | 109 | securityUser.setFirstName(claims.get(FIRST_NAME, String.class)); |
105 | securityUser.setLastName(claims.get(LAST_NAME, String.class)); | 110 | securityUser.setLastName(claims.get(LAST_NAME, String.class)); |
106 | securityUser.setEnabled(claims.get(ENABLED, Boolean.class)); | 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 | String tenantId = claims.get(TENANT_ID, String.class); | 115 | String tenantId = claims.get(TENANT_ID, String.class); |
108 | if (tenantId != null) { | 116 | if (tenantId != null) { |
109 | securityUser.setTenantId(new TenantId(UUID.fromString(tenantId))); | 117 | securityUser.setTenantId(new TenantId(UUID.fromString(tenantId))); |
@@ -123,9 +131,11 @@ public class JwtTokenFactory { | @@ -123,9 +131,11 @@ public class JwtTokenFactory { | ||
123 | 131 | ||
124 | DateTime currentTime = new DateTime(); | 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 | claims.put(SCOPES, Arrays.asList(Authority.REFRESH_TOKEN.name())); | 136 | claims.put(SCOPES, Arrays.asList(Authority.REFRESH_TOKEN.name())); |
128 | claims.put(USER_ID, securityUser.getId().getId().toString()); | 137 | claims.put(USER_ID, securityUser.getId().getId().toString()); |
138 | + claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID); | ||
129 | 139 | ||
130 | String token = Jwts.builder() | 140 | String token = Jwts.builder() |
131 | .setClaims(claims) | 141 | .setClaims(claims) |
@@ -150,8 +160,10 @@ public class JwtTokenFactory { | @@ -150,8 +160,10 @@ public class JwtTokenFactory { | ||
150 | if (!scopes.get(0).equals(Authority.REFRESH_TOKEN.name())) { | 160 | if (!scopes.get(0).equals(Authority.REFRESH_TOKEN.name())) { |
151 | throw new IllegalArgumentException("Invalid Refresh Token scope"); | 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 | SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class)))); | 165 | SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class)))); |
154 | - securityUser.setEmail(subject); | 166 | + securityUser.setUserPrincipal(principal); |
155 | return securityUser; | 167 | return securityUser; |
156 | } | 168 | } |
157 | 169 |
@@ -60,8 +60,8 @@ plugins: | @@ -60,8 +60,8 @@ plugins: | ||
60 | 60 | ||
61 | # JWT Token parameters | 61 | # JWT Token parameters |
62 | security.jwt: | 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 | tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}" | 65 | tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}" |
66 | tokenSigningKey: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}" | 66 | tokenSigningKey: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}" |
67 | 67 | ||
@@ -189,11 +189,33 @@ cache: | @@ -189,11 +189,33 @@ cache: | ||
189 | updates: | 189 | updates: |
190 | # Enable/disable updates checking. | 190 | # Enable/disable updates checking. |
191 | enabled: "${UPDATES_ENABLED:true}" | 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 | # SQL DAO Configuration | 215 | # SQL DAO Configuration |
194 | sql: | 216 | sql: |
195 | enabled: "${SQL_ENABLED:false}" | 217 | enabled: "${SQL_ENABLED:false}" |
196 | datasource: | 218 | datasource: |
197 | url: "${SQL_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard}" | 219 | url: "${SQL_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard}" |
198 | username: "${SQL_DATASOURCE_USERNAME:postgres}" | 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,7 +145,7 @@ public class DefaultActorServiceTest { | ||
145 | ReflectionTestUtils.setField(actorContext, "eventService", eventService); | 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 | when(discoveryService.getCurrentServer()).thenReturn(serverInstance); | 150 | when(discoveryService.getCurrentServer()).thenReturn(serverInstance); |
151 | 151 | ||
@@ -239,7 +239,7 @@ public class DefaultActorServiceTest { | @@ -239,7 +239,7 @@ public class DefaultActorServiceTest { | ||
239 | List<TsKvEntry> expected = new ArrayList<>(); | 239 | List<TsKvEntry> expected = new ArrayList<>(); |
240 | expected.add(new BasicTsKvEntry(ts, entry1)); | 240 | expected.add(new BasicTsKvEntry(ts, entry1)); |
241 | expected.add(new BasicTsKvEntry(ts, entry2)); | 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,7 +288,7 @@ public class CustomerControllerTest extends AbstractControllerTest { | ||
288 | for (int i=0;i<143;i++) { | 288 | for (int i=0;i<143;i++) { |
289 | Customer customer = new Customer(); | 289 | Customer customer = new Customer(); |
290 | customer.setTenantId(tenantId); | 290 | customer.setTenantId(tenantId); |
291 | - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); | 291 | + String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10)); |
292 | String title = title1+suffix; | 292 | String title = title1+suffix; |
293 | title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | 293 | title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); |
294 | customer.setTitle(title); | 294 | customer.setTitle(title); |
@@ -299,7 +299,7 @@ public class CustomerControllerTest extends AbstractControllerTest { | @@ -299,7 +299,7 @@ public class CustomerControllerTest extends AbstractControllerTest { | ||
299 | for (int i=0;i<175;i++) { | 299 | for (int i=0;i<175;i++) { |
300 | Customer customer = new Customer(); | 300 | Customer customer = new Customer(); |
301 | customer.setTenantId(tenantId); | 301 | customer.setTenantId(tenantId); |
302 | - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); | 302 | + String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10)); |
303 | String title = title2+suffix; | 303 | String title = title2+suffix; |
304 | title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | 304 | title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); |
305 | customer.setTitle(title); | 305 | customer.setTitle(title); |
@@ -149,7 +149,7 @@ public class TenantControllerTest extends AbstractControllerTest { | @@ -149,7 +149,7 @@ public class TenantControllerTest extends AbstractControllerTest { | ||
149 | List<Tenant> tenantsTitle1 = new ArrayList<>(); | 149 | List<Tenant> tenantsTitle1 = new ArrayList<>(); |
150 | for (int i=0;i<134;i++) { | 150 | for (int i=0;i<134;i++) { |
151 | Tenant tenant = new Tenant(); | 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 | String title = title1+suffix; | 153 | String title = title1+suffix; |
154 | title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | 154 | title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); |
155 | tenant.setTitle(title); | 155 | tenant.setTitle(title); |
@@ -159,7 +159,7 @@ public class TenantControllerTest extends AbstractControllerTest { | @@ -159,7 +159,7 @@ public class TenantControllerTest extends AbstractControllerTest { | ||
159 | List<Tenant> tenantsTitle2 = new ArrayList<>(); | 159 | List<Tenant> tenantsTitle2 = new ArrayList<>(); |
160 | for (int i=0;i<127;i++) { | 160 | for (int i=0;i<127;i++) { |
161 | Tenant tenant = new Tenant(); | 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 | String title = title2+suffix; | 163 | String title = title2+suffix; |
164 | title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | 164 | title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); |
165 | tenant.setTitle(title); | 165 | tenant.setTitle(title); |
@@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; | @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; | ||
19 | * @author Andrew Shvayka | 19 | * @author Andrew Shvayka |
20 | */ | 20 | */ |
21 | public enum EntityType { | 21 | public enum EntityType { |
22 | - TENANT, DEVICE, CUSTOMER, RULE, PLUGIN | 22 | + TENANT, CUSTOMER, USER, RULE, PLUGIN, DASHBOARD, ASSET, DEVICE, ALARM |
23 | } | 23 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.alarm; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import lombok.AllArgsConstructor; | ||
20 | +import lombok.Builder; | ||
21 | +import lombok.Data; | ||
22 | +import org.thingsboard.server.common.data.BaseData; | ||
23 | +import org.thingsboard.server.common.data.id.AssetId; | ||
24 | +import org.thingsboard.server.common.data.id.EntityId; | ||
25 | +import org.thingsboard.server.common.data.id.TenantId; | ||
26 | + | ||
27 | +/** | ||
28 | + * Created by ashvayka on 11.05.17. | ||
29 | + */ | ||
30 | +@Data | ||
31 | +@Builder | ||
32 | +@AllArgsConstructor | ||
33 | +public class Alarm extends BaseData<AlarmId> { | ||
34 | + | ||
35 | + private TenantId tenantId; | ||
36 | + private String type; | ||
37 | + private EntityId originator; | ||
38 | + private AlarmSeverity severity; | ||
39 | + private AlarmStatus status; | ||
40 | + private long startTs; | ||
41 | + private long endTs; | ||
42 | + private long ackTs; | ||
43 | + private long clearTs; | ||
44 | + private JsonNode details; | ||
45 | + private boolean propagate; | ||
46 | + | ||
47 | + public Alarm() { | ||
48 | + super(); | ||
49 | + } | ||
50 | + | ||
51 | + public Alarm(AlarmId id) { | ||
52 | + super(id); | ||
53 | + } | ||
54 | + | ||
55 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.alarm; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | ||
19 | +import com.fasterxml.jackson.annotation.JsonIgnore; | ||
20 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
21 | +import org.thingsboard.server.common.data.EntityType; | ||
22 | +import org.thingsboard.server.common.data.id.EntityId; | ||
23 | +import org.thingsboard.server.common.data.id.UUIDBased; | ||
24 | + | ||
25 | +import java.util.UUID; | ||
26 | + | ||
27 | +public class AlarmId extends UUIDBased implements EntityId { | ||
28 | + | ||
29 | + private static final long serialVersionUID = 1L; | ||
30 | + | ||
31 | + @JsonCreator | ||
32 | + public AlarmId(@JsonProperty("id") UUID id) { | ||
33 | + super(id); | ||
34 | + } | ||
35 | + | ||
36 | + public static AlarmId fromString(String alarmId) { | ||
37 | + return new AlarmId(UUID.fromString(alarmId)); | ||
38 | + } | ||
39 | + | ||
40 | + @JsonIgnore | ||
41 | + @Override | ||
42 | + public EntityType getEntityType() { | ||
43 | + return EntityType.ALARM; | ||
44 | + } | ||
45 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.alarm; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import org.thingsboard.server.common.data.id.EntityId; | ||
20 | +import org.thingsboard.server.common.data.id.TenantId; | ||
21 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
22 | + | ||
23 | +/** | ||
24 | + * Created by ashvayka on 11.05.17. | ||
25 | + */ | ||
26 | +@Data | ||
27 | +public class AlarmQuery { | ||
28 | + | ||
29 | + private TenantId tenantId; | ||
30 | + private EntityId affectedEntityId; | ||
31 | + private TimePageLink pageLink; | ||
32 | + private AlarmStatus status; | ||
33 | + | ||
34 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.alarm; | ||
17 | + | ||
18 | +/** | ||
19 | + * Created by ashvayka on 11.05.17. | ||
20 | + */ | ||
21 | +public enum AlarmSeverity { | ||
22 | + | ||
23 | + CRITICAL, MAJOR, MINOR, WARNING, INDETERMINATE; | ||
24 | + | ||
25 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.alarm; | ||
17 | + | ||
18 | +/** | ||
19 | + * Created by ashvayka on 11.05.17. | ||
20 | + */ | ||
21 | +public enum AlarmStatus { | ||
22 | + | ||
23 | + ACTIVE_UNACK, ACTIVE_ACK, CLEARED_UNACK, CLEARED_ACK; | ||
24 | + | ||
25 | + public boolean isAck() { | ||
26 | + return this == ACTIVE_ACK || this == CLEARED_ACK; | ||
27 | + } | ||
28 | + | ||
29 | + public boolean isCleared() { | ||
30 | + return this == CLEARED_ACK || this == CLEARED_UNACK; | ||
31 | + } | ||
32 | + | ||
33 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.asset; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import org.thingsboard.server.common.data.SearchTextBased; | ||
20 | +import org.thingsboard.server.common.data.id.AssetId; | ||
21 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
22 | +import org.thingsboard.server.common.data.id.TenantId; | ||
23 | + | ||
24 | +public class Asset extends SearchTextBased<AssetId> { | ||
25 | + | ||
26 | + private static final long serialVersionUID = 2807343040519543363L; | ||
27 | + | ||
28 | + private TenantId tenantId; | ||
29 | + private CustomerId customerId; | ||
30 | + private String name; | ||
31 | + private String type; | ||
32 | + private JsonNode additionalInfo; | ||
33 | + | ||
34 | + public Asset() { | ||
35 | + super(); | ||
36 | + } | ||
37 | + | ||
38 | + public Asset(AssetId id) { | ||
39 | + super(id); | ||
40 | + } | ||
41 | + | ||
42 | + public Asset(Asset asset) { | ||
43 | + super(asset); | ||
44 | + this.tenantId = asset.getTenantId(); | ||
45 | + this.customerId = asset.getCustomerId(); | ||
46 | + this.name = asset.getName(); | ||
47 | + this.type = asset.getType(); | ||
48 | + this.additionalInfo = asset.getAdditionalInfo(); | ||
49 | + } | ||
50 | + | ||
51 | + public TenantId getTenantId() { | ||
52 | + return tenantId; | ||
53 | + } | ||
54 | + | ||
55 | + public void setTenantId(TenantId tenantId) { | ||
56 | + this.tenantId = tenantId; | ||
57 | + } | ||
58 | + | ||
59 | + public CustomerId getCustomerId() { | ||
60 | + return customerId; | ||
61 | + } | ||
62 | + | ||
63 | + public void setCustomerId(CustomerId customerId) { | ||
64 | + this.customerId = customerId; | ||
65 | + } | ||
66 | + | ||
67 | + public String getName() { | ||
68 | + return name; | ||
69 | + } | ||
70 | + | ||
71 | + public void setName(String name) { | ||
72 | + this.name = name; | ||
73 | + } | ||
74 | + | ||
75 | + public String getType() { | ||
76 | + return type; | ||
77 | + } | ||
78 | + | ||
79 | + public void setType(String type) { | ||
80 | + this.type = type; | ||
81 | + } | ||
82 | + | ||
83 | + public JsonNode getAdditionalInfo() { | ||
84 | + return additionalInfo; | ||
85 | + } | ||
86 | + | ||
87 | + public void setAdditionalInfo(JsonNode additionalInfo) { | ||
88 | + this.additionalInfo = additionalInfo; | ||
89 | + } | ||
90 | + | ||
91 | + @Override | ||
92 | + public String getSearchText() { | ||
93 | + return name; | ||
94 | + } | ||
95 | + | ||
96 | + @Override | ||
97 | + public int hashCode() { | ||
98 | + final int prime = 31; | ||
99 | + int result = super.hashCode(); | ||
100 | + result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode()); | ||
101 | + result = prime * result + ((customerId == null) ? 0 : customerId.hashCode()); | ||
102 | + result = prime * result + ((name == null) ? 0 : name.hashCode()); | ||
103 | + result = prime * result + ((type == null) ? 0 : type.hashCode()); | ||
104 | + result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); | ||
105 | + return result; | ||
106 | + } | ||
107 | + | ||
108 | + @Override | ||
109 | + public boolean equals(Object obj) { | ||
110 | + if (this == obj) | ||
111 | + return true; | ||
112 | + if (!super.equals(obj)) | ||
113 | + return false; | ||
114 | + if (getClass() != obj.getClass()) | ||
115 | + return false; | ||
116 | + Asset other = (Asset) obj; | ||
117 | + if (additionalInfo == null) { | ||
118 | + if (other.additionalInfo != null) | ||
119 | + return false; | ||
120 | + } else if (!additionalInfo.equals(other.additionalInfo)) | ||
121 | + return false; | ||
122 | + if (customerId == null) { | ||
123 | + if (other.customerId != null) | ||
124 | + return false; | ||
125 | + } else if (!customerId.equals(other.customerId)) | ||
126 | + return false; | ||
127 | + if (name == null) { | ||
128 | + if (other.name != null) | ||
129 | + return false; | ||
130 | + } else if (!name.equals(other.name)) | ||
131 | + return false; | ||
132 | + if (type == null) { | ||
133 | + if (other.type != null) | ||
134 | + return false; | ||
135 | + } else if (!type.equals(other.type)) | ||
136 | + return false; | ||
137 | + if (tenantId == null) { | ||
138 | + if (other.tenantId != null) | ||
139 | + return false; | ||
140 | + } else if (!tenantId.equals(other.tenantId)) | ||
141 | + return false; | ||
142 | + return true; | ||
143 | + } | ||
144 | + | ||
145 | + @Override | ||
146 | + public String toString() { | ||
147 | + StringBuilder builder = new StringBuilder(); | ||
148 | + builder.append("Asset [tenantId="); | ||
149 | + builder.append(tenantId); | ||
150 | + builder.append(", customerId="); | ||
151 | + builder.append(customerId); | ||
152 | + builder.append(", name="); | ||
153 | + builder.append(name); | ||
154 | + builder.append(", type="); | ||
155 | + builder.append(type); | ||
156 | + builder.append(", additionalInfo="); | ||
157 | + builder.append(additionalInfo); | ||
158 | + builder.append(", createdTime="); | ||
159 | + builder.append(createdTime); | ||
160 | + builder.append(", id="); | ||
161 | + builder.append(id); | ||
162 | + builder.append("]"); | ||
163 | + return builder.toString(); | ||
164 | + } | ||
165 | + | ||
166 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.id; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | ||
19 | +import com.fasterxml.jackson.annotation.JsonIgnore; | ||
20 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
21 | +import org.thingsboard.server.common.data.EntityType; | ||
22 | + | ||
23 | +import java.util.UUID; | ||
24 | + | ||
25 | +public class AssetId extends UUIDBased implements EntityId { | ||
26 | + | ||
27 | + private static final long serialVersionUID = 1L; | ||
28 | + | ||
29 | + @JsonCreator | ||
30 | + public AssetId(@JsonProperty("id") UUID id) { | ||
31 | + super(id); | ||
32 | + } | ||
33 | + | ||
34 | + public static AssetId fromString(String assetId) { | ||
35 | + return new AssetId(UUID.fromString(assetId)); | ||
36 | + } | ||
37 | + | ||
38 | + @JsonIgnore | ||
39 | + @Override | ||
40 | + public EntityType getEntityType() { | ||
41 | + return EntityType.ASSET; | ||
42 | + } | ||
43 | +} |
@@ -18,12 +18,24 @@ package org.thingsboard.server.common.data.id; | @@ -18,12 +18,24 @@ package org.thingsboard.server.common.data.id; | ||
18 | import java.util.UUID; | 18 | import java.util.UUID; |
19 | 19 | ||
20 | import com.fasterxml.jackson.annotation.JsonCreator; | 20 | import com.fasterxml.jackson.annotation.JsonCreator; |
21 | +import com.fasterxml.jackson.annotation.JsonIgnore; | ||
21 | import com.fasterxml.jackson.annotation.JsonProperty; | 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 | @JsonCreator | 27 | @JsonCreator |
26 | - public DashboardId(@JsonProperty("id") UUID id){ | 28 | + public DashboardId(@JsonProperty("id") UUID id) { |
27 | super(id); | 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,6 +16,8 @@ | ||
16 | package org.thingsboard.server.common.data.id; | 16 | package org.thingsboard.server.common.data.id; |
17 | 17 | ||
18 | import com.fasterxml.jackson.annotation.JsonIgnore; | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; |
19 | +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | ||
20 | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; | ||
19 | import org.thingsboard.server.common.data.EntityType; | 21 | import org.thingsboard.server.common.data.EntityType; |
20 | 22 | ||
21 | import java.util.UUID; | 23 | import java.util.UUID; |
@@ -23,6 +25,9 @@ import java.util.UUID; | @@ -23,6 +25,9 @@ import java.util.UUID; | ||
23 | /** | 25 | /** |
24 | * @author Andrew Shvayka | 26 | * @author Andrew Shvayka |
25 | */ | 27 | */ |
28 | + | ||
29 | +@JsonDeserialize(using = EntityIdDeserializer.class) | ||
30 | +@JsonSerialize(using = EntityIdSerializer.class) | ||
26 | public interface EntityId { | 31 | public interface EntityId { |
27 | 32 | ||
28 | UUID NULL_UUID = UUID.fromString("13814000-1dd2-11b2-8080-808080808080"); | 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,12 +18,25 @@ package org.thingsboard.server.common.data.id; | ||
18 | import java.util.UUID; | 18 | import java.util.UUID; |
19 | 19 | ||
20 | import com.fasterxml.jackson.annotation.JsonCreator; | 20 | import com.fasterxml.jackson.annotation.JsonCreator; |
21 | +import com.fasterxml.jackson.annotation.JsonIgnore; | ||
21 | import com.fasterxml.jackson.annotation.JsonProperty; | 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 | @JsonCreator | 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,4 +187,14 @@ public abstract class CassandraAbstractModelDao<E extends BaseEntity<D>, D> exte | ||
187 | List<E> entities = findListByStatement(QueryBuilder.select().all().from(getColumnFamilyName()).setConsistencyLevel(cluster.getDefaultReadConsistencyLevel())); | 187 | List<E> entities = findListByStatement(QueryBuilder.select().all().from(getColumnFamilyName()).setConsistencyLevel(cluster.getDefaultReadConsistencyLevel())); |
188 | return DaoUtil.convertDataList(entities); | 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,6 +15,13 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.customer; | 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 | import lombok.extern.slf4j.Slf4j; | 25 | import lombok.extern.slf4j.Slf4j; |
19 | import org.springframework.stereotype.Component; | 26 | import org.springframework.stereotype.Component; |
20 | import org.thingsboard.server.common.data.Customer; | 27 | import org.thingsboard.server.common.data.Customer; |
@@ -53,4 +60,15 @@ public class CassandraCustomerDao extends CassandraAbstractSearchTextDao<Custome | @@ -53,4 +60,15 @@ public class CassandraCustomerDao extends CassandraAbstractSearchTextDao<Custome | ||
53 | return DaoUtil.convertDataList(customerEntities); | 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,6 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.customer; | 16 | package org.thingsboard.server.dao.customer; |
17 | 17 | ||
18 | +import java.util.Optional; | ||
18 | import org.thingsboard.server.common.data.Customer; | 19 | import org.thingsboard.server.common.data.Customer; |
19 | import org.thingsboard.server.common.data.page.TextPageLink; | 20 | import org.thingsboard.server.common.data.page.TextPageLink; |
20 | import org.thingsboard.server.dao.Dao; | 21 | import org.thingsboard.server.dao.Dao; |
@@ -43,5 +44,14 @@ public interface CustomerDao extends Dao<Customer> { | @@ -43,5 +44,14 @@ public interface CustomerDao extends Dao<Customer> { | ||
43 | * @return the list of customer objects | 44 | * @return the list of customer objects |
44 | */ | 45 | */ |
45 | List<Customer> findCustomersByTenantId(UUID tenantId, TextPageLink pageLink); | 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,6 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.customer; | 16 | package org.thingsboard.server.dao.customer; |
17 | 17 | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
18 | import org.thingsboard.server.common.data.Customer; | 19 | import org.thingsboard.server.common.data.Customer; |
19 | import org.thingsboard.server.common.data.id.CustomerId; | 20 | import org.thingsboard.server.common.data.id.CustomerId; |
20 | import org.thingsboard.server.common.data.id.TenantId; | 21 | import org.thingsboard.server.common.data.id.TenantId; |
@@ -24,13 +25,17 @@ import org.thingsboard.server.common.data.page.TextPageLink; | @@ -24,13 +25,17 @@ import org.thingsboard.server.common.data.page.TextPageLink; | ||
24 | public interface CustomerService { | 25 | public interface CustomerService { |
25 | 26 | ||
26 | Customer findCustomerById(CustomerId customerId); | 27 | Customer findCustomerById(CustomerId customerId); |
27 | - | 28 | + |
29 | + ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId); | ||
30 | + | ||
28 | Customer saveCustomer(Customer customer); | 31 | Customer saveCustomer(Customer customer); |
29 | 32 | ||
30 | void deleteCustomer(CustomerId customerId); | 33 | void deleteCustomer(CustomerId customerId); |
31 | - | 34 | + |
35 | + Customer findOrCreatePublicCustomer(TenantId tenantId); | ||
36 | + | ||
32 | TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink); | 37 | TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink); |
33 | 38 | ||
34 | void deleteCustomersByTenantId(TenantId tenantId); | 39 | void deleteCustomersByTenantId(TenantId tenantId); |
35 | - | 40 | + |
36 | } | 41 | } |
@@ -15,30 +15,42 @@ | @@ -15,30 +15,42 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.customer; | 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 | import lombok.extern.slf4j.Slf4j; | 27 | import lombok.extern.slf4j.Slf4j; |
19 | import org.apache.commons.lang3.StringUtils; | 28 | import org.apache.commons.lang3.StringUtils; |
20 | import org.springframework.beans.factory.annotation.Autowired; | 29 | import org.springframework.beans.factory.annotation.Autowired; |
21 | import org.springframework.stereotype.Service; | 30 | import org.springframework.stereotype.Service; |
22 | import org.thingsboard.server.common.data.Customer; | 31 | import org.thingsboard.server.common.data.Customer; |
23 | import org.thingsboard.server.common.data.Tenant; | 32 | import org.thingsboard.server.common.data.Tenant; |
33 | +import org.thingsboard.server.common.data.asset.Asset; | ||
24 | import org.thingsboard.server.common.data.id.CustomerId; | 34 | import org.thingsboard.server.common.data.id.CustomerId; |
25 | import org.thingsboard.server.common.data.id.TenantId; | 35 | import org.thingsboard.server.common.data.id.TenantId; |
26 | import org.thingsboard.server.common.data.page.TextPageData; | 36 | import org.thingsboard.server.common.data.page.TextPageData; |
27 | import org.thingsboard.server.common.data.page.TextPageLink; | 37 | import org.thingsboard.server.common.data.page.TextPageLink; |
28 | import org.thingsboard.server.dao.dashboard.DashboardService; | 38 | import org.thingsboard.server.dao.dashboard.DashboardService; |
29 | import org.thingsboard.server.dao.device.DeviceService; | 39 | import org.thingsboard.server.dao.device.DeviceService; |
40 | +import org.thingsboard.server.dao.entity.BaseEntityService; | ||
30 | import org.thingsboard.server.dao.exception.DataValidationException; | 41 | import org.thingsboard.server.dao.exception.DataValidationException; |
31 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 42 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
43 | +import org.thingsboard.server.dao.model.AssetEntity; | ||
32 | import org.thingsboard.server.dao.service.DataValidator; | 44 | import org.thingsboard.server.dao.service.DataValidator; |
33 | import org.thingsboard.server.dao.service.PaginatedRemover; | 45 | import org.thingsboard.server.dao.service.PaginatedRemover; |
34 | import org.thingsboard.server.dao.service.Validator; | 46 | import org.thingsboard.server.dao.service.Validator; |
35 | import org.thingsboard.server.dao.tenant.TenantDao; | 47 | import org.thingsboard.server.dao.tenant.TenantDao; |
36 | import org.thingsboard.server.dao.user.UserService; | 48 | import org.thingsboard.server.dao.user.UserService; |
37 | - | ||
38 | -import java.util.List; | ||
39 | @Service | 49 | @Service |
40 | @Slf4j | 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 | @Autowired | 55 | @Autowired |
44 | private CustomerDao customerDao; | 56 | private CustomerDao customerDao; |
@@ -63,6 +75,13 @@ public class CustomerServiceImpl implements CustomerService { | @@ -63,6 +75,13 @@ public class CustomerServiceImpl implements CustomerService { | ||
63 | } | 75 | } |
64 | 76 | ||
65 | @Override | 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 | public Customer saveCustomer(Customer customer) { | 85 | public Customer saveCustomer(Customer customer) { |
67 | log.trace("Executing saveCustomer [{}]", customer); | 86 | log.trace("Executing saveCustomer [{}]", customer); |
68 | customerValidator.validate(customer); | 87 | customerValidator.validate(customer); |
@@ -72,24 +91,45 @@ public class CustomerServiceImpl implements CustomerService { | @@ -72,24 +91,45 @@ public class CustomerServiceImpl implements CustomerService { | ||
72 | @Override | 91 | @Override |
73 | public void deleteCustomer(CustomerId customerId) { | 92 | public void deleteCustomer(CustomerId customerId) { |
74 | log.trace("Executing deleteCustomer [{}]", customerId); | 93 | log.trace("Executing deleteCustomer [{}]", customerId); |
75 | - Validator.validateId(customerId, "Incorrect tenantId " + customerId); | 94 | + Validator.validateId(customerId, "Incorrect customerId " + customerId); |
76 | Customer customer = findCustomerById(customerId); | 95 | Customer customer = findCustomerById(customerId); |
77 | if (customer == null) { | 96 | if (customer == null) { |
78 | throw new IncorrectParameterException("Unable to delete non-existent customer."); | 97 | throw new IncorrectParameterException("Unable to delete non-existent customer."); |
79 | } | 98 | } |
80 | dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId); | 99 | dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId); |
81 | deviceService.unassignCustomerDevices(customer.getTenantId(), customerId); | 100 | deviceService.unassignCustomerDevices(customer.getTenantId(), customerId); |
82 | - userService.deleteCustomerUsers(customer.getTenantId(), customerId); | 101 | + userService.deleteCustomerUsers(customer.getTenantId(), customerId); |
102 | + deleteEntityRelations(customerId); | ||
83 | customerDao.removeById(customerId.getId()); | 103 | customerDao.removeById(customerId.getId()); |
84 | } | 104 | } |
85 | 105 | ||
86 | @Override | 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 | public TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink) { | 127 | public TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink) { |
88 | log.trace("Executing findCustomersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); | 128 | log.trace("Executing findCustomersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); |
89 | Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); | 129 | Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); |
90 | Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); | 130 | Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); |
91 | List<Customer> customers = customerDao.findCustomersByTenantId(tenantId.getId(), pageLink); | 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 | @Override | 135 | @Override |
@@ -101,11 +141,35 @@ public class CustomerServiceImpl implements CustomerService { | @@ -101,11 +141,35 @@ public class CustomerServiceImpl implements CustomerService { | ||
101 | 141 | ||
102 | private DataValidator<Customer> customerValidator = | 142 | private DataValidator<Customer> customerValidator = |
103 | new DataValidator<Customer>() { | 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 | @Override | 165 | @Override |
105 | protected void validateDataImpl(Customer customer) { | 166 | protected void validateDataImpl(Customer customer) { |
106 | if (StringUtils.isEmpty(customer.getTitle())) { | 167 | if (StringUtils.isEmpty(customer.getTitle())) { |
107 | throw new DataValidationException("Customer title should be specified!"); | 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 | if (!StringUtils.isEmpty(customer.getEmail())) { | 173 | if (!StringUtils.isEmpty(customer.getEmail())) { |
110 | validateEmail(customer.getEmail()); | 174 | validateEmail(customer.getEmail()); |
111 | } | 175 | } |
@@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.id.TenantId; | @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.id.TenantId; | ||
29 | import org.thingsboard.server.common.data.page.TextPageData; | 29 | import org.thingsboard.server.common.data.page.TextPageData; |
30 | import org.thingsboard.server.common.data.page.TextPageLink; | 30 | import org.thingsboard.server.common.data.page.TextPageLink; |
31 | import org.thingsboard.server.dao.customer.CustomerDao; | 31 | import org.thingsboard.server.dao.customer.CustomerDao; |
32 | +import org.thingsboard.server.dao.entity.BaseEntityService; | ||
32 | import org.thingsboard.server.dao.exception.DataValidationException; | 33 | import org.thingsboard.server.dao.exception.DataValidationException; |
33 | import org.thingsboard.server.dao.model.ModelConstants; | 34 | import org.thingsboard.server.dao.model.ModelConstants; |
34 | import org.thingsboard.server.dao.service.DataValidator; | 35 | import org.thingsboard.server.dao.service.DataValidator; |
@@ -40,7 +41,7 @@ import java.util.List; | @@ -40,7 +41,7 @@ import java.util.List; | ||
40 | 41 | ||
41 | @Service | 42 | @Service |
42 | @Slf4j | 43 | @Slf4j |
43 | -public class DashboardServiceImpl implements DashboardService { | 44 | +public class DashboardServiceImpl extends BaseEntityService implements DashboardService { |
44 | 45 | ||
45 | @Autowired | 46 | @Autowired |
46 | private DashboardDao dashboardDao; | 47 | private DashboardDao dashboardDao; |
@@ -86,6 +87,7 @@ public class DashboardServiceImpl implements DashboardService { | @@ -86,6 +87,7 @@ public class DashboardServiceImpl implements DashboardService { | ||
86 | public void deleteDashboard(DashboardId dashboardId) { | 87 | public void deleteDashboard(DashboardId dashboardId) { |
87 | log.trace("Executing deleteDashboard [{}]", dashboardId); | 88 | log.trace("Executing deleteDashboard [{}]", dashboardId); |
88 | Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId); | 89 | Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId); |
90 | + deleteEntityRelations(dashboardId); | ||
89 | dashboardDao.removeById(dashboardId.getId()); | 91 | dashboardDao.removeById(dashboardId.getId()); |
90 | } | 92 | } |
91 | 93 |
@@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.page.TextPageLink; | @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.page.TextPageLink; | ||
32 | import org.thingsboard.server.common.data.security.DeviceCredentials; | 32 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
33 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; | 33 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
34 | import org.thingsboard.server.dao.customer.CustomerDao; | 34 | import org.thingsboard.server.dao.customer.CustomerDao; |
35 | +import org.thingsboard.server.dao.entity.BaseEntityService; | ||
35 | import org.thingsboard.server.dao.exception.DataValidationException; | 36 | import org.thingsboard.server.dao.exception.DataValidationException; |
36 | import org.thingsboard.server.dao.service.DataValidator; | 37 | import org.thingsboard.server.dao.service.DataValidator; |
37 | import org.thingsboard.server.dao.service.PaginatedRemover; | 38 | import org.thingsboard.server.dao.service.PaginatedRemover; |
@@ -48,7 +49,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink; | @@ -48,7 +49,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink; | ||
48 | 49 | ||
49 | @Service | 50 | @Service |
50 | @Slf4j | 51 | @Slf4j |
51 | -public class DeviceServiceImpl implements DeviceService { | 52 | +public class DeviceServiceImpl extends BaseEntityService implements DeviceService { |
52 | 53 | ||
53 | @Autowired | 54 | @Autowired |
54 | private DeviceDao deviceDao; | 55 | private DeviceDao deviceDao; |
@@ -125,6 +126,7 @@ public class DeviceServiceImpl implements DeviceService { | @@ -125,6 +126,7 @@ public class DeviceServiceImpl implements DeviceService { | ||
125 | if (deviceCredentials != null) { | 126 | if (deviceCredentials != null) { |
126 | deviceCredentialsService.deleteDeviceCredentials(deviceCredentials); | 127 | deviceCredentialsService.deleteDeviceCredentials(deviceCredentials); |
127 | } | 128 | } |
129 | + deleteEntityRelations(deviceId); | ||
128 | deviceDao.removeById(deviceId.getId()); | 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 | +} |
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 | +} |
@@ -112,6 +112,7 @@ public class ModelConstants { | @@ -112,6 +112,7 @@ public class ModelConstants { | ||
112 | public static final String CUSTOMER_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; | 112 | public static final String CUSTOMER_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; |
113 | 113 | ||
114 | public static final String CUSTOMER_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "customer_by_tenant_and_search_text"; | 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 | * Cassandra device constants. | 118 | * Cassandra device constants. |
@@ -126,6 +127,51 @@ public class ModelConstants { | @@ -126,6 +127,51 @@ public class ModelConstants { | ||
126 | public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text"; | 127 | public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text"; |
127 | public static final String DEVICE_BY_TENANT_AND_NAME_VIEW_NAME = "device_by_tenant_and_name"; | 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 | * Cassandra device_credentials constants. | 177 | * Cassandra device_credentials constants. |
@@ -99,23 +99,7 @@ public class EventEntity implements BaseEntity<Event> { | @@ -99,23 +99,7 @@ public class EventEntity implements BaseEntity<Event> { | ||
99 | Event event = new Event(new EventId(id)); | 99 | Event event = new Event(new EventId(id)); |
100 | event.setCreatedTime(UUIDs.unixTimestamp(id)); | 100 | event.setCreatedTime(UUIDs.unixTimestamp(id)); |
101 | event.setTenantId(new TenantId(tenantId)); | 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 | event.setBody(body); | 103 | event.setBody(body); |
120 | event.setType(eventType); | 104 | event.setType(eventType); |
121 | event.setUid(eventUid); | 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,11 +15,14 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.plugin; | 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 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
19 | import org.apache.commons.lang3.StringUtils; | 21 | import org.apache.commons.lang3.StringUtils; |
20 | import org.springframework.beans.factory.annotation.Autowired; | 22 | import org.springframework.beans.factory.annotation.Autowired; |
21 | import org.springframework.stereotype.Service; | 23 | import org.springframework.stereotype.Service; |
22 | import org.thingsboard.server.common.data.id.PluginId; | 24 | import org.thingsboard.server.common.data.id.PluginId; |
25 | +import org.thingsboard.server.common.data.id.RuleId; | ||
23 | import org.thingsboard.server.common.data.id.TenantId; | 26 | import org.thingsboard.server.common.data.id.TenantId; |
24 | import org.thingsboard.server.common.data.page.TextPageData; | 27 | import org.thingsboard.server.common.data.page.TextPageData; |
25 | import org.thingsboard.server.common.data.page.TextPageLink; | 28 | import org.thingsboard.server.common.data.page.TextPageLink; |
@@ -29,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; | @@ -29,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; | ||
29 | import org.thingsboard.server.common.data.plugin.PluginMetaData; | 32 | import org.thingsboard.server.common.data.plugin.PluginMetaData; |
30 | import org.thingsboard.server.common.data.rule.RuleMetaData; | 33 | import org.thingsboard.server.common.data.rule.RuleMetaData; |
31 | import org.thingsboard.server.dao.component.ComponentDescriptorService; | 34 | import org.thingsboard.server.dao.component.ComponentDescriptorService; |
35 | +import org.thingsboard.server.dao.entity.BaseEntityService; | ||
32 | import org.thingsboard.server.dao.exception.DataValidationException; | 36 | import org.thingsboard.server.dao.exception.DataValidationException; |
33 | import org.thingsboard.server.dao.exception.DatabaseException; | 37 | import org.thingsboard.server.dao.exception.DatabaseException; |
34 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 38 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
@@ -43,9 +47,10 @@ import java.util.List; | @@ -43,9 +47,10 @@ import java.util.List; | ||
43 | import java.util.UUID; | 47 | import java.util.UUID; |
44 | import java.util.stream.Collectors; | 48 | import java.util.stream.Collectors; |
45 | 49 | ||
50 | +import static org.thingsboard.server.dao.service.Validator.validateId; | ||
46 | @Service | 51 | @Service |
47 | @Slf4j | 52 | @Slf4j |
48 | -public class BasePluginService implements PluginService { | 53 | +public class BasePluginService extends BaseEntityService implements PluginService { |
49 | 54 | ||
50 | //TODO: move to a better place. | 55 | //TODO: move to a better place. |
51 | public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); | 56 | public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); |
@@ -103,6 +108,12 @@ public class BasePluginService implements PluginService { | @@ -103,6 +108,12 @@ public class BasePluginService implements PluginService { | ||
103 | } | 108 | } |
104 | 109 | ||
105 | @Override | 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 | public PluginMetaData findPluginByApiToken(String apiToken) { | 117 | public PluginMetaData findPluginByApiToken(String apiToken) { |
107 | Validator.validateString(apiToken, "Incorrect plugin apiToken for search request."); | 118 | Validator.validateString(apiToken, "Incorrect plugin apiToken for search request."); |
108 | return pluginDao.findByApiToken(apiToken); | 119 | return pluginDao.findByApiToken(apiToken); |
@@ -196,6 +207,7 @@ public class BasePluginService implements PluginService { | @@ -196,6 +207,7 @@ public class BasePluginService implements PluginService { | ||
196 | @Override | 207 | @Override |
197 | public void deletePluginById(PluginId pluginId) { | 208 | public void deletePluginById(PluginId pluginId) { |
198 | Validator.validateId(pluginId, "Incorrect plugin id for delete request."); | 209 | Validator.validateId(pluginId, "Incorrect plugin id for delete request."); |
210 | + deleteEntityRelations(pluginId); | ||
199 | checkRulesAndDelete(pluginId.getId()); | 211 | checkRulesAndDelete(pluginId.getId()); |
200 | } | 212 | } |
201 | 213 |
@@ -15,6 +15,7 @@ | @@ -15,6 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.plugin; | 16 | package org.thingsboard.server.dao.plugin; |
17 | 17 | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
18 | import org.thingsboard.server.common.data.id.PluginId; | 19 | import org.thingsboard.server.common.data.id.PluginId; |
19 | import org.thingsboard.server.common.data.id.TenantId; | 20 | import org.thingsboard.server.common.data.id.TenantId; |
20 | import org.thingsboard.server.common.data.page.TextPageData; | 21 | import org.thingsboard.server.common.data.page.TextPageData; |
@@ -29,6 +30,8 @@ public interface PluginService { | @@ -29,6 +30,8 @@ public interface PluginService { | ||
29 | 30 | ||
30 | PluginMetaData findPluginById(PluginId pluginId); | 31 | PluginMetaData findPluginById(PluginId pluginId); |
31 | 32 | ||
33 | + ListenableFuture<PluginMetaData> findPluginByIdAsync(PluginId pluginId); | ||
34 | + | ||
32 | PluginMetaData findPluginByApiToken(String apiToken); | 35 | PluginMetaData findPluginByApiToken(String apiToken); |
33 | 36 | ||
34 | TextPageData<PluginMetaData> findSystemPlugins(TextPageLink pageLink); | 37 | TextPageData<PluginMetaData> findSystemPlugins(TextPageLink pageLink); |