- +
@@ -55,10 +55,18 @@
- - - - +
+ + + + + +
search diff --git a/ui/src/app/layout/user-menu.directive.js b/ui/src/app/layout/user-menu.directive.js index 6d09e679ba..53e8c45b8b 100644 --- a/ui/src/app/layout/user-menu.directive.js +++ b/ui/src/app/layout/user-menu.directive.js @@ -48,16 +48,10 @@ function UserMenuController($scope, userService, $translate, $state) { var dashboardUser = userService.getCurrentUser(); vm.authorityName = authorityName; - vm.displaySearchMode = displaySearchMode; vm.logout = logout; vm.openProfile = openProfile; vm.userDisplayName = userDisplayName; - function displaySearchMode() { - return $scope.searchConfig.searchEnabled && - $scope.searchConfig.showSearch; - } - function authorityName() { var name = "user.anonymous"; if (dashboardUser) { diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index 622754cf70..50f6d8f5c7 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -124,6 +124,9 @@ export default angular.module('thingsboard.locale', []) "unassign-from-customer": "Unassign from customer", "delete": "Delete asset", "asset-public": "Asset is public", + "asset-type": "Asset type", + "asset-type-required": "Asset type is required.", + "select-asset-type": "Select asset type", "name": "Name", "name-required": "Name is required.", "description": "Description", @@ -477,6 +480,9 @@ export default angular.module('thingsboard.locale', []) "rsa-key-required": "RSA public key is required.", "secret": "Secret", "secret-required": "Secret is required.", + "device-type": "Device type", + "device-type-required": "Device type is required.", + "select-device-type": "Select device type", "name": "Name", "name-required": "Name is required.", "description": "Description", @@ -521,6 +527,7 @@ export default angular.module('thingsboard.locale', []) "entity-list-empty": "No entities selected.", "entity-name-filter-required": "Entity name filter is required.", "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", + "all-subtypes": "All", "type": "Type", "type-device": "Device", "type-asset": "Asset", From 2e618ee6748a3135f10c3e4f42df95f312da2fe8 Mon Sep 17 00:00:00 2001 From: volodymyr-babak Date: Mon, 29 May 2017 10:33:33 +0300 Subject: [PATCH 34/37] Revert changes for the config parameters --- application/src/main/resources/thingsboard.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 24cea0e4af..4b488af7bb 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -66,10 +66,10 @@ plugins: # JWT Token parameters security.jwt: - token-expiration-time: "${JWT_TOKEN_EXPIRATION_TIME:9000000}" # Number of seconds (15 mins) - refresh-token-exp-time: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:36000000}" # Seconds (1 hour) - token-issuer: "${JWT_TOKEN_ISSUER:thingsboard.io}" - token-signing-key: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}" + tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:9000000}" # Number of seconds (15 mins) + refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:36000000}" # Seconds (1 hour) + tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}" + tokenSigningKey: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}" # Device communication protocol parameters http: From a708509aacbab0a723833c29b28a0775a2f6ac77 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 29 May 2017 11:47:43 +0300 Subject: [PATCH 35/37] Entity relations management. --- .../controller/EntityRelationController.java | 16 ++ .../server/common/data/Customer.java | 7 +- .../server/common/data/DashboardInfo.java | 7 +- .../server/common/data/Device.java | 3 +- .../server/common/data/HasName.java | 22 +++ .../server/common/data/Tenant.java | 7 +- .../thingsboard/server/common/data/User.java | 7 +- .../server/common/data/alarm/Alarm.java | 7 +- .../server/common/data/asset/Asset.java | 4 +- .../common/data/plugin/PluginMetaData.java | 4 +- .../common/data/relation/EntityRelation.java | 10 +- .../data/relation/EntityRelationInfo.java | 59 ++++++ .../server/common/data/rule/RuleMetaData.java | 8 +- .../server/dao/alarm/AlarmService.java | 4 + .../server/dao/alarm/BaseAlarmService.java | 66 +++++++ .../server/dao/asset/BaseAssetService.java | 4 +- .../dao/customer/CustomerServiceImpl.java | 6 +- .../dao/dashboard/DashboardService.java | 5 + .../dao/dashboard/DashboardServiceImpl.java | 24 ++- .../server/dao/device/DeviceServiceImpl.java | 4 +- .../dao/entity/AbstractEntityService.java | 36 ++++ .../server/dao/entity/BaseEntityService.java | 93 ++++++++- .../server/dao/entity/EntityService.java | 28 +++ .../server/dao/plugin/BasePluginService.java | 6 +- .../dao/relation/BaseRelationService.java | 43 +++++ .../server/dao/relation/RelationService.java | 3 + .../server/dao/rule/BaseRuleService.java | 6 +- .../server/dao/tenant/TenantServiceImpl.java | 6 +- .../server/dao/user/UserService.java | 3 + .../server/dao/user/UserServiceImpl.java | 15 +- ui/src/app/api/entity-relation.service.js | 13 ++ ui/src/app/api/entity.service.js | 113 ++++++----- ui/src/app/asset/assets.tpl.html | 6 + ui/src/app/common/types.constant.js | 5 +- ui/src/app/common/utils.service.js | 26 ++- .../dashboard-autocomplete.tpl.html | 2 +- ui/src/app/components/plugin-select.tpl.html | 2 +- .../states/entity-state-controller.js | 2 +- .../entity/attribute/attribute-table.tpl.html | 6 +- .../app/entity/entity-aliases.controller.js | 2 +- .../entity/entity-autocomplete.directive.js | 171 +++++++++++++++++ ui/src/app/entity/entity-autocomplete.scss | 25 +++ .../app/entity/entity-autocomplete.tpl.html | 45 +++++ ui/src/app/entity/entity-filter.directive.js | 8 - ui/src/app/entity/entity-filter.tpl.html | 2 +- ui/src/app/entity/entity-select.directive.js | 86 +++++++++ ui/src/app/entity/entity-select.scss | 19 ++ ui/src/app/entity/entity-select.tpl.html | 29 +++ .../entity/entity-type-select.directive.js | 19 +- ui/src/app/entity/index.js | 6 + .../add-relation-dialog.controller.js | 43 +++++ .../relation/add-relation-dialog.tpl.html | 64 +++++++ .../relation/relation-table.directive.js | 179 ++++++++++++++++++ .../app/entity/relation/relation-table.scss | 28 +++ .../entity/relation/relation-table.tpl.html | 119 ++++++++++++ ui/src/app/locale/locale.constant-es.js | 6 +- ui/src/app/locale/locale.constant-ko.js | 6 +- ui/src/app/locale/locale.constant-ru.js | 6 +- ui/src/app/locale/locale.constant-zh.js | 6 +- ui/src/app/locale/locale.constant.js | 65 ++++++- ui/src/scss/main.scss | 39 ++++ 61 files changed, 1516 insertions(+), 145 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/HasName.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/entity/EntityService.java create mode 100644 ui/src/app/entity/entity-autocomplete.directive.js create mode 100644 ui/src/app/entity/entity-autocomplete.scss create mode 100644 ui/src/app/entity/entity-autocomplete.tpl.html create mode 100644 ui/src/app/entity/entity-select.directive.js create mode 100644 ui/src/app/entity/entity-select.scss create mode 100644 ui/src/app/entity/entity-select.tpl.html create mode 100644 ui/src/app/entity/relation/add-relation-dialog.controller.js create mode 100644 ui/src/app/entity/relation/add-relation-dialog.tpl.html create mode 100644 ui/src/app/entity/relation/relation-table.directive.js create mode 100644 ui/src/app/entity/relation/relation-table.scss create mode 100644 ui/src/app/entity/relation/relation-table.tpl.html diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 0c1fd8bf73..04f1c3ffc7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.dao.relation.EntityRelationsQuery; import org.thingsboard.server.exception.ThingsboardErrorCode; import org.thingsboard.server.exception.ThingsboardException; @@ -127,6 +128,21 @@ public class EntityRelationController extends BaseController { } } + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"fromId", "fromType"}) + @ResponseBody + public List findInfoByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType) throws ThingsboardException { + checkParameter("fromId", strFromId); + checkParameter("fromType", strFromType); + EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); + checkEntityId(entityId); + try { + return checkNotNull(relationService.findInfoByFrom(entityId).get()); + } catch (Exception e) { + throw handleException(e); + } + } + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"}) @ResponseBody diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index 71a45270b8..f8c1e3045a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -20,7 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId; import com.fasterxml.jackson.databind.JsonNode; -public class Customer extends ContactBased{ +public class Customer extends ContactBased implements HasName { private static final long serialVersionUID = -1599722990298929275L; @@ -59,6 +59,11 @@ public class Customer extends ContactBased{ this.title = title; } + @Override + public String getName() { + return title; + } + public JsonNode getAdditionalInfo() { return additionalInfo; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index d88df160f8..c4daf9d85c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; -public class DashboardInfo extends SearchTextBased { +public class DashboardInfo extends SearchTextBased implements HasName { private TenantId tenantId; private CustomerId customerId; @@ -64,6 +64,11 @@ public class DashboardInfo extends SearchTextBased { this.title = title; } + @Override + public String getName() { + return title; + } + @Override public String getSearchText() { return title; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 92e8655c72..7d3d4f5742 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.id.TenantId; import com.fasterxml.jackson.databind.JsonNode; -public class Device extends SearchTextBased { +public class Device extends SearchTextBased implements HasName { private static final long serialVersionUID = 2807343040519543363L; @@ -64,6 +64,7 @@ public class Device extends SearchTextBased { this.customerId = customerId; } + @Override public String getName() { return name; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/HasName.java b/common/data/src/main/java/org/thingsboard/server/common/data/HasName.java new file mode 100644 index 0000000000..f43198908e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/HasName.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +public interface HasName { + + String getName(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java index aee1510608..3a5f6bf09a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java @@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.id.TenantId; import com.fasterxml.jackson.databind.JsonNode; -public class Tenant extends ContactBased{ +public class Tenant extends ContactBased implements HasName { private static final long serialVersionUID = 8057243243859922101L; @@ -50,6 +50,11 @@ public class Tenant extends ContactBased{ this.title = title; } + @Override + public String getName() { + return title; + } + public String getRegion() { return region; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index 74fa4cff96..0543ef0a38 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.security.Authority; import com.fasterxml.jackson.databind.JsonNode; -public class User extends SearchTextBased { +public class User extends SearchTextBased implements HasName { private static final long serialVersionUID = 8250339805336035966L; @@ -77,6 +77,11 @@ public class User extends SearchTextBased { this.email = email; } + @Override + public String getName() { + return email; + } + public Authority getAuthority() { return authority; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index 19d671cab2..4bef0079b3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -18,13 +18,14 @@ package org.thingsboard.server.common.data.alarm; import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.id.EntityId; /** * Created by ashvayka on 11.05.17. */ @Data -public class Alarm extends BaseData { +public class Alarm extends BaseData implements HasName { private long startTs; private long endTs; @@ -37,4 +38,8 @@ public class Alarm extends BaseData { private JsonNode details; private boolean propagate; + @Override + public String getName() { + return type; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index 2e20b7745c..dafa514f61 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -16,12 +16,13 @@ package org.thingsboard.server.common.data.asset; import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.SearchTextBased; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; -public class Asset extends SearchTextBased { +public class Asset extends SearchTextBased implements HasName { private static final long serialVersionUID = 2807343040519543363L; @@ -64,6 +65,7 @@ public class Asset extends SearchTextBased { this.customerId = customerId; } + @Override public String getName() { return name; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java index 5019cd119f..e5eb149fbc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java @@ -15,13 +15,14 @@ */ package org.thingsboard.server.common.data.plugin; +import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.SearchTextBased; import org.thingsboard.server.common.data.id.PluginId; import org.thingsboard.server.common.data.id.TenantId; import com.fasterxml.jackson.databind.JsonNode; -public class PluginMetaData extends SearchTextBased { +public class PluginMetaData extends SearchTextBased implements HasName { private static final long serialVersionUID = 1L; @@ -75,6 +76,7 @@ public class PluginMetaData extends SearchTextBased { this.tenantId = tenantId; } + @Override public String getName() { return name; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java index 8fcf2691af..39e7d54978 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java @@ -47,11 +47,11 @@ public class EntityRelation { this.additionalInfo = additionalInfo; } - public EntityRelation(EntityRelation device) { - this.from = device.getFrom(); - this.to = device.getTo(); - this.type = device.getType(); - this.additionalInfo = device.getAdditionalInfo(); + public EntityRelation(EntityRelation entityRelation) { + this.from = entityRelation.getFrom(); + this.to = entityRelation.getTo(); + this.type = entityRelation.getType(); + this.additionalInfo = entityRelation.getAdditionalInfo(); } public EntityId getFrom() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java new file mode 100644 index 0000000000..709ad7937a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thingsboard.server.common.data.relation; + +public class EntityRelationInfo extends EntityRelation { + + private static final long serialVersionUID = 2807343097519543363L; + + private String toName; + + public EntityRelationInfo() { + super(); + } + + public EntityRelationInfo(EntityRelation entityRelation) { + super(entityRelation); + } + + public String getToName() { + return toName; + } + + public void setToName(String toName) { + this.toName = toName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + EntityRelationInfo that = (EntityRelationInfo) o; + + return toName != null ? toName.equals(that.toName) : that.toName == null; + + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (toName != null ? toName.hashCode() : 0); + return result; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java index ecbc86cd6e..8a0f847617 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.rule; import lombok.Data; import lombok.ToString; +import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.SearchTextBased; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.RuleId; @@ -26,7 +27,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; @Data -public class RuleMetaData extends SearchTextBased { +public class RuleMetaData extends SearchTextBased implements HasName { private static final long serialVersionUID = -5656679015122935465L; @@ -66,4 +67,9 @@ public class RuleMetaData extends SearchTextBased { return name; } + @Override + public String getName() { + return name; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index 35bd247602..dca229b2a5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -28,6 +28,10 @@ import java.util.Optional; */ public interface AlarmService { + Alarm findAlarmById(AlarmId alarmId); + + ListenableFuture findAlarmByIdAsync(AlarmId alarmId); + Optional saveIfNotExists(Alarm alarm); ListenableFuture updateAlarm(Alarm alarm); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java new file mode 100644 index 0000000000..5ed15b753f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -0,0 +1,66 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.alarm; + +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmId; +import org.thingsboard.server.common.data.alarm.AlarmQuery; +import org.thingsboard.server.common.data.page.TimePageData; + +import java.util.Optional; + +@Service +@Slf4j +public class BaseAlarmService implements AlarmService { + + @Override + public Alarm findAlarmById(AlarmId alarmId) { + return null; + } + + @Override + public ListenableFuture findAlarmByIdAsync(AlarmId alarmId) { + return null; + } + + @Override + public Optional saveIfNotExists(Alarm alarm) { + return null; + } + + @Override + public ListenableFuture updateAlarm(Alarm alarm) { + return null; + } + + @Override + public ListenableFuture ackAlarm(Alarm alarm) { + return null; + } + + @Override + public ListenableFuture clearAlarm(AlarmId alarmId) { + return null; + } + + @Override + public ListenableFuture> findAlarms(AlarmQuery query) { + return null; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index fce0c5eefd..3a5f803046 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.dao.customer.CustomerDao; -import org.thingsboard.server.dao.entity.BaseEntityService; +import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.*; import org.thingsboard.server.dao.relation.EntitySearchDirection; @@ -55,7 +55,7 @@ import static org.thingsboard.server.dao.service.Validator.*; @Service @Slf4j -public class BaseAssetService extends BaseEntityService implements AssetService { +public class BaseAssetService extends AbstractEntityService implements AssetService { @Autowired private AssetDao assetDao; diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index e9f6836774..ff726dafb2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -31,17 +31,15 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.entity.BaseEntityService; +import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.model.AssetEntity; import org.thingsboard.server.dao.model.CustomerEntity; import org.thingsboard.server.dao.model.TenantEntity; import org.thingsboard.server.dao.service.DataValidator; @@ -53,7 +51,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.dao.service.Validator; @Service @Slf4j -public class CustomerServiceImpl extends BaseEntityService implements CustomerService { +public class CustomerServiceImpl extends AbstractEntityService implements CustomerService { private static final String PUBLIC_CUSTOMER_TITLE = "Public"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java index b0ebbfd5f0..f1f174eb0b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.dashboard; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.id.CustomerId; @@ -27,8 +28,12 @@ public interface DashboardService { public Dashboard findDashboardById(DashboardId dashboardId); + public ListenableFuture findDashboardByIdAsync(DashboardId dashboardId); + public DashboardInfo findDashboardInfoById(DashboardId dashboardId); + public ListenableFuture findDashboardInfoByIdAsync(DashboardId dashboardId); + public Dashboard saveDashboard(Dashboard dashboard); public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index cf554f3a62..fc3e176e00 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -17,9 +17,13 @@ package org.thingsboard.server.dao.dashboard; import static org.thingsboard.server.dao.DaoUtil.convertDataList; import static org.thingsboard.server.dao.DaoUtil.getData; +import static org.thingsboard.server.dao.service.Validator.validateId; import java.util.List; +import com.google.common.base.Function; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.Dashboard; @@ -30,7 +34,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.customer.CustomerDao; -import org.thingsboard.server.dao.entity.BaseEntityService; +import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.*; import org.thingsboard.server.dao.service.DataValidator; @@ -42,7 +46,7 @@ import org.thingsboard.server.dao.service.Validator; @Service @Slf4j -public class DashboardServiceImpl extends BaseEntityService implements DashboardService { +public class DashboardServiceImpl extends AbstractEntityService implements DashboardService { @Autowired private DashboardDao dashboardDao; @@ -64,6 +68,14 @@ public class DashboardServiceImpl extends BaseEntityService implements Dashboard return getData(dashboardEntity); } + @Override + public ListenableFuture findDashboardByIdAsync(DashboardId dashboardId) { + log.trace("Executing findDashboardByIdAsync [{}]", dashboardId); + validateId(dashboardId, "Incorrect dashboardId " + dashboardId); + ListenableFuture dashboardEntity = dashboardDao.findByIdAsync(dashboardId.getId()); + return Futures.transform(dashboardEntity, (Function) input -> getData(input)); + } + @Override public DashboardInfo findDashboardInfoById(DashboardId dashboardId) { log.trace("Executing findDashboardInfoById [{}]", dashboardId); @@ -72,6 +84,14 @@ public class DashboardServiceImpl extends BaseEntityService implements Dashboard return getData(dashboardInfoEntity); } + @Override + public ListenableFuture findDashboardInfoByIdAsync(DashboardId dashboardId) { + log.trace("Executing findDashboardInfoByIdAsync [{}]", dashboardId); + validateId(dashboardId, "Incorrect dashboardId " + dashboardId); + ListenableFuture dashboardInfoEntity = dashboardInfoDao.findByIdAsync(dashboardId.getId()); + return Futures.transform(dashboardInfoEntity, (Function) input -> getData(input)); + } + @Override public Dashboard saveDashboard(Dashboard dashboard) { log.trace("Executing saveDashboard [{}]", dashboard); diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index d01f154197..99585ea76f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -37,7 +37,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.dao.customer.CustomerDao; -import org.thingsboard.server.dao.entity.BaseEntityService; +import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.CustomerEntity; import org.thingsboard.server.dao.model.DeviceEntity; @@ -58,7 +58,7 @@ import static org.thingsboard.server.dao.service.Validator.*; @Service @Slf4j -public class DeviceServiceImpl extends BaseEntityService implements DeviceService { +public class DeviceServiceImpl extends AbstractEntityService implements DeviceService { @Autowired private DeviceDao deviceDao; diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java new file mode 100644 index 0000000000..ecca491070 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thingsboard.server.dao.entity; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.dao.relation.RelationService; + +@Slf4j +public abstract class AbstractEntityService { + + @Autowired + protected RelationService relationService; + + protected void deleteEntityRelations(EntityId entityId) { + log.trace("Executing deleteEntityRelations [{}]", entityId); + relationService.deleteEntityRelations(entityId); + } + + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 3c1c528193..6f9500ed3b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -15,23 +15,102 @@ */ package org.thingsboard.server.dao.entity; +import com.google.common.base.Function; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.dao.relation.RelationService; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.*; +import org.thingsboard.server.common.data.alarm.AlarmId; +import org.thingsboard.server.common.data.id.*; +import org.thingsboard.server.dao.alarm.AlarmService; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.plugin.PluginService; +import org.thingsboard.server.dao.rule.RuleService; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.user.UserService; /** * Created by ashvayka on 04.05.17. */ +@Service @Slf4j -public class BaseEntityService { +public class BaseEntityService extends AbstractEntityService implements EntityService { @Autowired - protected RelationService relationService; + private AssetService assetService; - protected void deleteEntityRelations(EntityId entityId) { - log.trace("Executing deleteEntityRelations [{}]", entityId); - relationService.deleteEntityRelations(entityId); + @Autowired + private DeviceService deviceService; + + @Autowired + private RuleService ruleService; + + @Autowired + private PluginService pluginService; + + @Autowired + private TenantService tenantService; + + @Autowired + private CustomerService customerService; + + @Autowired + private UserService userService; + + @Autowired + private DashboardService dashboardService; + + @Autowired + private AlarmService alarmService; + + @Override + public void deleteEntityRelations(EntityId entityId) { + super.deleteEntityRelations(entityId); + } + + @Override + public ListenableFuture fetchEntityNameAsync(EntityId entityId) { + log.trace("Executing fetchEntityNameAsync [{}]", entityId); + ListenableFuture entityName; + ListenableFuture hasName; + switch (entityId.getEntityType()) { + case ASSET: + hasName = assetService.findAssetByIdAsync(new AssetId(entityId.getId())); + break; + case DEVICE: + hasName = deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); + break; + case RULE: + hasName = ruleService.findRuleByIdAsync(new RuleId(entityId.getId())); + break; + case PLUGIN: + hasName = pluginService.findPluginByIdAsync(new PluginId(entityId.getId())); + break; + case TENANT: + hasName = tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); + break; + case CUSTOMER: + hasName = customerService.findCustomerByIdAsync(new CustomerId(entityId.getId())); + break; + case USER: + hasName = userService.findUserByIdAsync(new UserId(entityId.getId())); + break; + case DASHBOARD: + hasName = dashboardService.findDashboardInfoByIdAsync(new DashboardId(entityId.getId())); + break; + case ALARM: + hasName = alarmService.findAlarmByIdAsync(new AlarmId(entityId.getId())); + break; + default: + throw new IllegalStateException("Not Implemented!"); + } + entityName = Futures.transform(hasName, (Function) hasName1 -> hasName1.getName() ); + return entityName; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/EntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/EntityService.java new file mode 100644 index 0000000000..415f5189f3 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/EntityService.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.thingsboard.server.dao.entity; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.id.EntityId; + +public interface EntityService { + + ListenableFuture fetchEntityNameAsync(EntityId entityId); + + void deleteEntityRelations(EntityId entityId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java b/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java index 5e0eb399f6..a0be00ef0d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java @@ -22,7 +22,6 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; @@ -30,9 +29,8 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; import org.thingsboard.server.dao.component.ComponentDescriptorService; -import org.thingsboard.server.dao.entity.BaseEntityService; +import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DatabaseException; import org.thingsboard.server.dao.exception.IncorrectParameterException; @@ -55,7 +53,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Service @Slf4j -public class BasePluginService extends BaseEntityService implements PluginService { +public class BasePluginService extends AbstractEntityService implements PluginService { //TODO: move to a better place. public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java index 1b572c64ad..e7d38e5d9b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java @@ -23,9 +23,24 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationInfo; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.plugin.PluginService; +import org.thingsboard.server.dao.rule.RuleService; +import org.thingsboard.server.dao.tenant.TenantService; import javax.annotation.Nullable; import java.util.*; @@ -41,6 +56,9 @@ public class BaseRelationService implements RelationService { @Autowired private RelationDao relationDao; + @Autowired + private EntityService entityService; + @Override public ListenableFuture checkRelation(EntityId from, EntityId to, String relationType) { log.trace("Executing checkRelation [{}][{}][{}]", from, to, relationType); @@ -99,6 +117,31 @@ public class BaseRelationService implements RelationService { return relationDao.findAllByFrom(from); } + @Override + public ListenableFuture> findInfoByFrom(EntityId from) { + log.trace("Executing findInfoByFrom [{}]", from); + validate(from); + ListenableFuture> relations = relationDao.findAllByFrom(from); + ListenableFuture> relationsInfo = Futures.transform(relations, + (AsyncFunction, List>) relations1 -> { + List> futures = new ArrayList<>(); + relations1.stream().forEach(relation -> futures.add(fetchRelationInfoAsync(relation))); + return Futures.successfulAsList(futures); + }); + return relationsInfo; + } + + private ListenableFuture fetchRelationInfoAsync(EntityRelation relation) { + ListenableFuture entityName = entityService.fetchEntityNameAsync(relation.getTo()); + ListenableFuture entityRelationInfo = + Futures.transform(entityName, (Function) entityName1 -> { + EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation); + entityRelationInfo1.setToName(entityName1); + return entityRelationInfo1; + }); + return entityRelationInfo; + } + @Override public ListenableFuture> findByFromAndType(EntityId from, String relationType) { log.trace("Executing findByFromAndType [{}][{}]", from, relationType); diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java index f4f3a37185..353e595865 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.relation; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationInfo; import java.util.List; @@ -38,6 +39,8 @@ public interface RelationService { ListenableFuture> findByFrom(EntityId from); + ListenableFuture> findInfoByFrom(EntityId from); + ListenableFuture> findByFromAndType(EntityId from, String relationType); ListenableFuture> findByTo(EntityId to); diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java index fb5e9ce146..350fc3a7cb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java @@ -23,7 +23,6 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.RuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; @@ -34,11 +33,10 @@ import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.PluginMetaData; import org.thingsboard.server.common.data.rule.RuleMetaData; import org.thingsboard.server.dao.component.ComponentDescriptorService; -import org.thingsboard.server.dao.entity.BaseEntityService; +import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DatabaseException; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.model.AssetEntity; import org.thingsboard.server.dao.model.RuleMetaDataEntity; import org.thingsboard.server.dao.plugin.PluginService; import org.thingsboard.server.dao.service.DataValidator; @@ -58,7 +56,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink; @Service @Slf4j -public class BaseRuleService extends BaseEntityService implements RuleService { +public class BaseRuleService extends AbstractEntityService implements RuleService { private final TenantId systemTenantId = new TenantId(NULL_UUID); diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index bcc31a835e..e6f5011124 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -26,7 +26,6 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; @@ -34,9 +33,8 @@ import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.entity.BaseEntityService; +import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.model.CustomerEntity; import org.thingsboard.server.dao.model.TenantEntity; import org.thingsboard.server.dao.plugin.PluginService; import org.thingsboard.server.dao.rule.RuleService; @@ -50,7 +48,7 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService; @Service @Slf4j -public class TenantServiceImpl extends BaseEntityService implements TenantService { +public class TenantServiceImpl extends AbstractEntityService implements TenantService { private static final String DEFAULT_TENANT_REGION = "Global"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserService.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserService.java index f4043a0d6e..47e800cfa2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.user; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -27,6 +28,8 @@ public interface UserService { public User findUserById(UserId userId); + public ListenableFuture findUserByIdAsync(UserId userId); + public User findUserByEmail(String email); public User saveUser(User user); diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 64101110b4..125040b019 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -23,6 +23,9 @@ import static org.thingsboard.server.dao.service.Validator.validateString; import java.util.List; +import com.google.common.base.Function; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; @@ -35,7 +38,7 @@ import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.customer.CustomerDao; -import org.thingsboard.server.dao.entity.BaseEntityService; +import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.*; @@ -47,7 +50,7 @@ import org.springframework.stereotype.Service; @Service @Slf4j -public class UserServiceImpl extends BaseEntityService implements UserService { +public class UserServiceImpl extends AbstractEntityService implements UserService { @Autowired private UserDao userDao; @@ -77,6 +80,14 @@ public class UserServiceImpl extends BaseEntityService implements UserService { return getData(userEntity); } + @Override + public ListenableFuture findUserByIdAsync(UserId userId) { + log.trace("Executing findUserByIdAsync [{}]", userId); + validateId(userId, "Incorrect userId " + userId); + ListenableFuture userEntity = userDao.findByIdAsync(userId.getId()); + return Futures.transform(userEntity, (Function) input -> getData(input)); + } + @Override public User saveUser(User user) { log.trace("Executing saveUser [{}]", user); diff --git a/ui/src/app/api/entity-relation.service.js b/ui/src/app/api/entity-relation.service.js index 998607ad65..703964594b 100644 --- a/ui/src/app/api/entity-relation.service.js +++ b/ui/src/app/api/entity-relation.service.js @@ -25,6 +25,7 @@ function EntityRelationService($http, $q) { deleteRelation: deleteRelation, deleteRelations: deleteRelations, findByFrom: findByFrom, + findInfoByFrom: findInfoByFrom, findByFromAndType: findByFromAndType, findByTo: findByTo, findByToAndType: findByToAndType, @@ -84,6 +85,18 @@ function EntityRelationService($http, $q) { return deferred.promise; } + function findInfoByFrom(fromId, fromType) { + var deferred = $q.defer(); + var url = '/api/relations/info?fromId=' + fromId; + url += '&fromType=' + fromType; + $http.get(url, null).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; + } + function findByFromAndType(fromId, fromType, relationType) { var deferred = $q.defer(); var url = '/api/relations?fromId=' + fromId; diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js index a70308b52f..891f9d83b2 100644 --- a/ui/src/app/api/entity.service.js +++ b/ui/src/app/api/entity.service.js @@ -20,14 +20,13 @@ export default angular.module('thingsboard.api.entity', [thingsboardTypes]) .name; /*@ngInject*/ -function EntityService($http, $q, $filter, $translate, userService, deviceService, +function EntityService($http, $q, $filter, $translate, $log, userService, deviceService, assetService, tenantService, customerService, - ruleService, pluginService, entityRelationService, attributeService, types, utils) { + ruleService, pluginService, dashboardService, entityRelationService, attributeService, types, utils) { var service = { getEntity: getEntity, getEntities: getEntities, getEntitiesByNameFilter: getEntitiesByNameFilter, - entityName: entityName, processEntityAliases: processEntityAliases, getEntityKeys: getEntityKeys, checkEntityAlias: checkEntityAlias, @@ -63,6 +62,15 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic case types.entityType.plugin: promise = pluginService.getPlugin(entityId); break; + case types.entityType.dashboard: + promise = dashboardService.getDashboardInfo(entityId); + break; + case types.entityType.user: + promise = userService.getUser(entityId); + break; + case types.entityType.alarm: + $log.error('Get Alarm Entity is not implemented!'); + break; } return promise; } @@ -134,6 +142,15 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic case types.entityType.plugin: promise = getEntitiesByIdsPromise(pluginService.getPlugin, entityIds); break; + case types.entityType.dashboard: + promise = getEntitiesByIdsPromise(dashboardService.getDashboardInfo, entityIds); + break; + case types.entityType.user: + promise = getEntitiesByIdsPromise(userService.getUser, entityIds); + break; + case types.entityType.alarm: + $log.error('Get Alarm Entity is not implemented!'); + break; } return promise; } @@ -141,34 +158,38 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic function getEntities(entityType, entityIds, config) { var deferred = $q.defer(); var promise = getEntitiesPromise(entityType, entityIds, config); - promise.then( - function success(result) { - deferred.resolve(result); - }, - function fail() { - deferred.reject(); - } - ); + if (promise) { + promise.then( + function success(result) { + deferred.resolve(result); + }, + function fail() { + deferred.reject(); + } + ); + } else { + deferred.reject(); + } return deferred.promise; } - function getEntitiesByPageLinkPromise(entityType, pageLink, config) { + function getEntitiesByPageLinkPromise(entityType, pageLink, config, subType) { var promise; var user = userService.getCurrentUser(); var customerId = user.customerId; switch (entityType) { case types.entityType.device: if (user.authority === 'CUSTOMER_USER') { - promise = deviceService.getCustomerDevices(customerId, pageLink, false, config); + promise = deviceService.getCustomerDevices(customerId, pageLink, false, config, subType); } else { - promise = deviceService.getTenantDevices(pageLink, false, config); + promise = deviceService.getTenantDevices(pageLink, false, config, subType); } break; case types.entityType.asset: if (user.authority === 'CUSTOMER_USER') { - promise = assetService.getCustomerAssets(customerId, pageLink, false, config); + promise = assetService.getCustomerAssets(customerId, pageLink, false, config, subType); } else { - promise = assetService.getTenantAssets(pageLink, false, config); + promise = assetService.getTenantAssets(pageLink, false, config, subType); } break; case types.entityType.tenant: @@ -183,48 +204,48 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic case types.entityType.plugin: promise = pluginService.getAllPlugins(pageLink); break; + case types.entityType.dashboard: + if (user.authority === 'CUSTOMER_USER') { + promise = dashboardService.getCustomerDashboards(customerId, pageLink); + } else { + promise = dashboardService.getTenantDashboards(pageLink); + } + break; + case types.entityType.user: + $log.error('Get User Entities is not implemented!'); + break; + case types.entityType.alarm: + $log.error('Get Alarm Entities is not implemented!'); + break; } return promise; } - function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config) { + function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) { var deferred = $q.defer(); var pageLink = {limit: limit, textSearch: entityNameFilter}; - var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config); - promise.then( - function success(result) { - if (result.data && result.data.length > 0) { - deferred.resolve(result.data); - } else { + var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType); + if (promise) { + promise.then( + function success(result) { + if (result.data && result.data.length > 0) { + deferred.resolve(result.data); + } else { + deferred.resolve(null); + } + }, + function fail() { deferred.resolve(null); } - }, - function fail() { - deferred.resolve(null); - } - ); - return deferred.promise; - } - - function entityName(entityType, entity) { - var name = ''; - switch (entityType) { - case types.entityType.device: - case types.entityType.asset: - case types.entityType.rule: - case types.entityType.plugin: - name = entity.name; - break; - case types.entityType.tenant: - case types.entityType.customer: - name = entity.title; - break; + ); + } else { + deferred.resolve(null); } - return name; + return deferred.promise; } function entityToEntityInfo(entityType, entity) { - return { name: entityName(entityType, entity), entityType: entityType, id: entity.id.id }; + return { name: entity.name, entityType: entityType, id: entity.id.id }; } function entitiesToEntitiesInfo(entityType, entities) { diff --git a/ui/src/app/asset/assets.tpl.html b/ui/src/app/asset/assets.tpl.html index fb3cf56c86..11a118f5e4 100644 --- a/ui/src/app/asset/assets.tpl.html +++ b/ui/src/app/asset/assets.tpl.html @@ -55,4 +55,10 @@ default-event-type="{{vm.types.eventType.alarm.value}}"> + + + + diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index a8d8556f38..47ac7a01ed 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -98,7 +98,10 @@ export default angular.module('thingsboard.types', []) rule: "RULE", plugin: "PLUGIN", tenant: "TENANT", - customer: "CUSTOMER" + customer: "CUSTOMER", + user: "USER", + dashboard: "DASHBOARD", + alarm: "ALARM" }, entitySearchDirection: { from: "FROM", diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js index b324cbfa45..7b4d65ef78 100644 --- a/ui/src/app/common/utils.service.js +++ b/ui/src/app/common/utils.service.js @@ -108,7 +108,8 @@ function Utils($mdColorPalette, $rootScope, $window, types) { guid: guid, isLocalUrl: isLocalUrl, validateDatasources: validateDatasources, - createKey: createKey + createKey: createKey, + entityTypeName: entityTypeName } return service; @@ -346,4 +347,27 @@ function Utils($mdColorPalette, $rootScope, $window, types) { return dataKey; } + function entityTypeName (type) { + switch (type) { + case types.entityType.device: + return 'entity.type-device'; + case types.entityType.asset: + return 'entity.type-asset'; + case types.entityType.rule: + return 'entity.type-rule'; + case types.entityType.plugin: + return 'entity.type-plugin'; + case types.entityType.tenant: + return 'entity.type-tenant'; + case types.entityType.customer: + return 'entity.type-customer'; + case types.entityType.user: + return 'entity.type-user'; + case types.entityType.dashboard: + return 'entity.type-dashboard'; + case types.entityType.alarm: + return 'entity.type-alarm'; + } + } + } diff --git a/ui/src/app/components/dashboard-autocomplete.tpl.html b/ui/src/app/components/dashboard-autocomplete.tpl.html index 8b6be20778..d1193c20ea 100644 --- a/ui/src/app/components/dashboard-autocomplete.tpl.html +++ b/ui/src/app/components/dashboard-autocomplete.tpl.html @@ -34,7 +34,7 @@
- dashboard.no-dashboards-matching + dashboard.no-dashboards-matching
diff --git a/ui/src/app/components/plugin-select.tpl.html b/ui/src/app/components/plugin-select.tpl.html index 420442ef4c..9a46d7f1cc 100644 --- a/ui/src/app/components/plugin-select.tpl.html +++ b/ui/src/app/components/plugin-select.tpl.html @@ -34,7 +34,7 @@
- plugin.no-plugins-matching + plugin.no-plugins-matching
diff --git a/ui/src/app/dashboard/states/entity-state-controller.js b/ui/src/app/dashboard/states/entity-state-controller.js index 3eaf453c33..8ee9285b5f 100644 --- a/ui/src/app/dashboard/states/entity-state-controller.js +++ b/ui/src/app/dashboard/states/entity-state-controller.js @@ -109,7 +109,7 @@ export default function EntityStateController($scope, $location, $state, $stateP if (params && params.entityId && params.entityId.id && params.entityId.entityType) { entityService.getEntity(params.entityId.entityType, params.entityId.id, {ignoreLoading: true, ignoreErrors: true}).then( function success(entity) { - var entityName = entityService.entityName(params.entityId.entityType, entity); + var entityName = entity.name; deferred.resolve(entityName); }, function fail() { diff --git a/ui/src/app/entity/attribute/attribute-table.tpl.html b/ui/src/app/entity/attribute/attribute-table.tpl.html index 3b10902242..1afd1bb452 100644 --- a/ui/src/app/entity/attribute/attribute-table.tpl.html +++ b/ui/src/app/entity/attribute/attribute-table.tpl.html @@ -128,9 +128,9 @@ - - - + + + diff --git a/ui/src/app/entity/entity-aliases.controller.js b/ui/src/app/entity/entity-aliases.controller.js index 4eb1737c0a..f1e2e38f6a 100644 --- a/ui/src/app/entity/entity-aliases.controller.js +++ b/ui/src/app/entity/entity-aliases.controller.js @@ -110,7 +110,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc entityAlias.changed = false; } if (!entityAlias.changed && entity && entityAlias.entityType) { - entityAlias.alias = entityService.entityName(entityAlias.entityType, entity); + entityAlias.alias = entity.name; } } } diff --git a/ui/src/app/entity/entity-autocomplete.directive.js b/ui/src/app/entity/entity-autocomplete.directive.js new file mode 100644 index 0000000000..350bde9b3e --- /dev/null +++ b/ui/src/app/entity/entity-autocomplete.directive.js @@ -0,0 +1,171 @@ +/* + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import './entity-autocomplete.scss'; + +/* eslint-disable import/no-unresolved, import/default */ + +import entityAutocompleteTemplate from './entity-autocomplete.tpl.html'; + +/* eslint-enable import/no-unresolved, import/default */ + +/*@ngInject*/ +export default function EntityAutocomplete($compile, $templateCache, $q, $filter, entityService, types) { + + var linker = function (scope, element, attrs, ngModelCtrl) { + var template = $templateCache.get(entityAutocompleteTemplate); + element.html(template); + + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; + scope.entity = null; + scope.entitySearchText = ''; + + scope.fetchEntities = function(searchText) { + var deferred = $q.defer(); + entityService.getEntitiesByNameFilter(scope.entityType, searchText, 50, null, scope.entitySubtype).then(function success(result) { + if (result) { + deferred.resolve(result); + } else { + deferred.resolve([]); + } + }, function fail() { + deferred.reject(); + }); + return deferred.promise; + } + + scope.entitySearchTextChanged = function() { + } + + scope.updateView = function () { + if (!scope.disabled) { + ngModelCtrl.$setViewValue(scope.entity ? scope.entity.id.id : null); + } + } + + ngModelCtrl.$render = function () { + if (ngModelCtrl.$viewValue) { + entityService.getEntity(scope.entityType, ngModelCtrl.$viewValue).then( + function success(entity) { + scope.entity = entity; + }, + function fail() { + scope.entity = null; + } + ); + } else { + scope.entity = null; + } + } + + scope.$watch('entityType', function () { + load(); + }); + + scope.$watch('entitySubtype', function () { + if (scope.entity && scope.entity.type != scope.entitySubtype) { + scope.entity = null; + scope.updateView(); + } + }); + + scope.$watch('entity', function () { + scope.updateView(); + }); + + scope.$watch('disabled', function () { + scope.updateView(); + }); + + + function load() { + switch (scope.entityType) { + case types.entityType.asset: + scope.selectEntityText = 'asset.select-asset'; + scope.entityText = 'asset.asset'; + scope.noEntitiesMatchingText = 'asset.no-assets-matching'; + scope.entityRequiredText = 'asset.asset-required' + break; + case types.entityType.device: + scope.selectEntityText = 'device.select-device'; + scope.entityText = 'device.device'; + scope.noEntitiesMatchingText = 'device.no-devices-matching'; + scope.entityRequiredText = 'device.device-required' + break; + case types.entityType.rule: + scope.selectEntityText = 'rule.select-rule'; + scope.entityText = 'rule.rule'; + scope.noEntitiesMatchingText = 'rule.no-rules-matching'; + scope.entityRequiredText = 'rule.rule-required' + break; + case types.entityType.plugin: + scope.selectEntityText = 'plugin.select-plugin'; + scope.entityText = 'plugin.plugin'; + scope.noEntitiesMatchingText = 'plugin.no-plugins-matching'; + scope.entityRequiredText = 'plugin.plugin-required' + break; + case types.entityType.tenant: + scope.selectEntityText = 'tenant.select-tenant'; + scope.entityText = 'tenant.tenant'; + scope.noEntitiesMatchingText = 'tenant.no-tenants-matching'; + scope.entityRequiredText = 'tenant.tenant-required' + break; + case types.entityType.customer: + scope.selectEntityText = 'customer.select-customer'; + scope.entityText = 'customer.customer'; + scope.noEntitiesMatchingText = 'customer.no-customers-matching'; + scope.entityRequiredText = 'customer.customer-required' + break; + case types.entityType.user: + scope.selectEntityText = 'user.select-user'; + scope.entityText = 'user.user'; + scope.noEntitiesMatchingText = 'user.no-users-matching'; + scope.entityRequiredText = 'user.user-required' + break; + case types.entityType.dashboard: + scope.selectEntityText = 'dashboard.select-dashboard'; + scope.entityText = 'dashboard.dashboard'; + scope.noEntitiesMatchingText = 'dashboard.no-dashboards-matching'; + scope.entityRequiredText = 'dashboard.dashboard-required' + break; + case types.entityType.alarm: + scope.selectEntityText = 'alarm.select-alarm'; + scope.entityText = 'alarm.alarm'; + scope.noEntitiesMatchingText = 'alarm.no-alarms-matching'; + scope.entityRequiredText = 'alarm.alarm-required' + break; + } + if (scope.entity && scope.entity.id.entityType != scope.entityType) { + scope.entity = null; + scope.updateView(); + } + } + + $compile(element.contents())(scope); + } + + return { + restrict: "E", + require: "^ngModel", + link: linker, + scope: { + theForm: '=?', + tbRequired: '=?', + disabled:'=ngDisabled', + entityType: '=', + entitySubtype: '=?' + } + }; +} diff --git a/ui/src/app/entity/entity-autocomplete.scss b/ui/src/app/entity/entity-autocomplete.scss new file mode 100644 index 0000000000..c6affa5c19 --- /dev/null +++ b/ui/src/app/entity/entity-autocomplete.scss @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-entity-autocomplete { + .tb-entity-item { + display: block; + height: 48px; + } + li { + height: auto !important; + white-space: normal !important; + } +} diff --git a/ui/src/app/entity/entity-autocomplete.tpl.html b/ui/src/app/entity/entity-autocomplete.tpl.html new file mode 100644 index 0000000000..ca6f37bcc3 --- /dev/null +++ b/ui/src/app/entity/entity-autocomplete.tpl.html @@ -0,0 +1,45 @@ + + + +
+ {{item.name}} +
+
+ +
+ {{ noEntitiesMatchingText }} +
+
+
+
{{ entityRequiredText }}
+
+
diff --git a/ui/src/app/entity/entity-filter.directive.js b/ui/src/app/entity/entity-filter.directive.js index 777dbb8996..b1d92c6cc2 100644 --- a/ui/src/app/entity/entity-filter.directive.js +++ b/ui/src/app/entity/entity-filter.directive.js @@ -32,14 +32,6 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti scope.ngModelCtrl = ngModelCtrl; - scope.itemName = function(item) { - if (item) { - return entityService.entityName(scope.entityType, item); - } else { - return ''; - } - } - scope.fetchEntities = function(searchText, limit) { var deferred = $q.defer(); entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(function success(result) { diff --git a/ui/src/app/entity/entity-filter.tpl.html b/ui/src/app/entity/entity-filter.tpl.html index 508e4ba42f..cbb997e40b 100644 --- a/ui/src/app/entity/entity-filter.tpl.html +++ b/ui/src/app/entity/entity-filter.tpl.html @@ -33,7 +33,7 @@ md-min-length="0" placeholder="{{ 'entity.entity-list' | translate }}"> - {{itemName(item)}} + {{item.name}} entity.no-entities-matching diff --git a/ui/src/app/entity/entity-select.directive.js b/ui/src/app/entity/entity-select.directive.js new file mode 100644 index 0000000000..8988aaeccd --- /dev/null +++ b/ui/src/app/entity/entity-select.directive.js @@ -0,0 +1,86 @@ +/* + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import './entity-select.scss'; + +/* eslint-disable import/no-unresolved, import/default */ + +import entitySelectTemplate from './entity-select.tpl.html'; + +/* eslint-enable import/no-unresolved, import/default */ + +/*@ngInject*/ +export default function EntitySelect($compile, $templateCache) { + + var linker = function (scope, element, attrs, ngModelCtrl) { + var template = $templateCache.get(entitySelectTemplate); + element.html(template); + + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; + scope.model = null; + + scope.updateView = function () { + if (!scope.disabled) { + var value = ngModelCtrl.$viewValue; + if (scope.model && scope.model.entityType && scope.model.entityId) { + if (!value) { + value = {}; + } + value.entityType = scope.model.entityType; + value.id = scope.model.entityId; + ngModelCtrl.$setViewValue(value); + } else { + ngModelCtrl.$setViewValue(null); + } + } + } + + ngModelCtrl.$render = function () { + if (ngModelCtrl.$viewValue) { + var value = ngModelCtrl.$viewValue; + scope.model = {}; + scope.model.entityType = value.entityType; + scope.model.entityId = value.id; + } else { + scope.model = null; + } + } + + scope.$watch('model.entityType', function () { + scope.updateView(); + }); + + scope.$watch('model.entityId', function () { + scope.updateView(); + }); + + scope.$watch('disabled', function () { + scope.updateView(); + }); + + $compile(element.contents())(scope); + } + + return { + restrict: "E", + require: "^ngModel", + link: linker, + scope: { + theForm: '=?', + tbRequired: '=?', + disabled:'=ngDisabled' + } + }; +} diff --git a/ui/src/app/entity/entity-select.scss b/ui/src/app/entity/entity-select.scss new file mode 100644 index 0000000000..166c5193fb --- /dev/null +++ b/ui/src/app/entity/entity-select.scss @@ -0,0 +1,19 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.tb-entity-select { + +} \ No newline at end of file diff --git a/ui/src/app/entity/entity-select.tpl.html b/ui/src/app/entity/entity-select.tpl.html new file mode 100644 index 0000000000..a354b1dcf6 --- /dev/null +++ b/ui/src/app/entity/entity-select.tpl.html @@ -0,0 +1,29 @@ + +
+ + + + +
\ No newline at end of file diff --git a/ui/src/app/entity/entity-type-select.directive.js b/ui/src/app/entity/entity-type-select.directive.js index 6a5a0457d6..3a595a33ff 100644 --- a/ui/src/app/entity/entity-type-select.directive.js +++ b/ui/src/app/entity/entity-type-select.directive.js @@ -23,7 +23,7 @@ import entityTypeSelectTemplate from './entity-type-select.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ /*@ngInject*/ -export default function EntityTypeSelect($compile, $templateCache, userService, types) { +export default function EntityTypeSelect($compile, $templateCache, utils, userService, types) { var linker = function (scope, element, attrs, ngModelCtrl) { var template = $templateCache.get(entityTypeSelectTemplate); @@ -51,10 +51,12 @@ export default function EntityTypeSelect($compile, $templateCache, userService, scope.entityTypes.customer = types.entityType.customer; scope.entityTypes.rule = types.entityType.rule; scope.entityTypes.plugin = types.entityType.plugin; + scope.entityTypes.dashboard = types.entityType.dashboard; break; case 'CUSTOMER_USER': scope.entityTypes.device = types.entityType.device; scope.entityTypes.asset = types.entityType.asset; + scope.entityTypes.dashboard = types.entityType.dashboard; break; } @@ -67,20 +69,7 @@ export default function EntityTypeSelect($compile, $templateCache, userService, } scope.typeName = function(type) { - switch (type) { - case types.entityType.device: - return 'entity.type-device'; - case types.entityType.asset: - return 'entity.type-asset'; - case types.entityType.rule: - return 'entity.type-rule'; - case types.entityType.plugin: - return 'entity.type-plugin'; - case types.entityType.tenant: - return 'entity.type-tenant'; - case types.entityType.customer: - return 'entity.type-customer'; - } + return utils.entityTypeName(type); } scope.updateValidity = function () { diff --git a/ui/src/app/entity/index.js b/ui/src/app/entity/index.js index 8423f11c3c..bed956208e 100644 --- a/ui/src/app/entity/index.js +++ b/ui/src/app/entity/index.js @@ -18,12 +18,15 @@ import EntityAliasesController from './entity-aliases.controller'; import EntityTypeSelectDirective from './entity-type-select.directive'; import EntitySubtypeSelectDirective from './entity-subtype-select.directive'; import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive'; +import EntityAutocompleteDirective from './entity-autocomplete.directive'; +import EntitySelectDirective from './entity-select.directive'; import EntityFilterDirective from './entity-filter.directive'; import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller'; import AliasesEntitySelectDirective from './aliases-entity-select.directive'; import AddAttributeDialogController from './attribute/add-attribute-dialog.controller'; import AddWidgetToDashboardDialogController from './attribute/add-widget-to-dashboard-dialog.controller'; import AttributeTableDirective from './attribute/attribute-table.directive'; +import RelationTableDirective from './relation/relation-table.directive'; export default angular.module('thingsboard.entity', []) .controller('EntityAliasesController', EntityAliasesController) @@ -33,7 +36,10 @@ export default angular.module('thingsboard.entity', []) .directive('tbEntityTypeSelect', EntityTypeSelectDirective) .directive('tbEntitySubtypeSelect', EntitySubtypeSelectDirective) .directive('tbEntitySubtypeAutocomplete', EntitySubtypeAutocompleteDirective) + .directive('tbEntityAutocomplete', EntityAutocompleteDirective) + .directive('tbEntitySelect', EntitySelectDirective) .directive('tbEntityFilter', EntityFilterDirective) .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) .directive('tbAttributeTable', AttributeTableDirective) + .directive('tbRelationTable', RelationTableDirective) .name; diff --git a/ui/src/app/entity/relation/add-relation-dialog.controller.js b/ui/src/app/entity/relation/add-relation-dialog.controller.js new file mode 100644 index 0000000000..c331946f7f --- /dev/null +++ b/ui/src/app/entity/relation/add-relation-dialog.controller.js @@ -0,0 +1,43 @@ +/* + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/*@ngInject*/ +export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, from) { + + var vm = this; + + vm.types = types; + + vm.relation = {}; + vm.relation.from = from; + vm.relation.type = types.entityRelationType.contains; + + vm.add = add; + vm.cancel = cancel; + + function cancel() { + $mdDialog.cancel(); + } + + function add() { + $scope.theForm.$setPristine(); + entityRelationService.saveRelation(vm.relation).then( + function success() { + $mdDialog.hide(); + } + ); + } + +} diff --git a/ui/src/app/entity/relation/add-relation-dialog.tpl.html b/ui/src/app/entity/relation/add-relation-dialog.tpl.html new file mode 100644 index 0000000000..b32d23a249 --- /dev/null +++ b/ui/src/app/entity/relation/add-relation-dialog.tpl.html @@ -0,0 +1,64 @@ + + +
+ +
+

relation.add

+ + + + +
+
+ + + +
+ +
+ + + + + {{('relation.relation-types.' + type) | translate}} + + + + {{'entity.entity' | translate }} + + +
+
+
+
+ + + + {{ 'action.add' | translate }} + + {{ 'action.cancel' | + translate }} + + + +
diff --git a/ui/src/app/entity/relation/relation-table.directive.js b/ui/src/app/entity/relation/relation-table.directive.js new file mode 100644 index 0000000000..bf4aa52ebe --- /dev/null +++ b/ui/src/app/entity/relation/relation-table.directive.js @@ -0,0 +1,179 @@ +/* + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import 'angular-material-data-table/dist/md-data-table.min.css'; +import './relation-table.scss'; + +/* eslint-disable import/no-unresolved, import/default */ + +import relationTableTemplate from './relation-table.tpl.html'; +import addRelationTemplate from './add-relation-dialog.tpl.html'; + +/* eslint-enable import/no-unresolved, import/default */ + +import AddRelationController from './add-relation-dialog.controller'; + +/*@ngInject*/ +export default function RelationTable() { + return { + restrict: "E", + scope: true, + bindToController: { + entityId: '=', + entityType: '@' + }, + controller: RelationTableController, + controllerAs: 'vm', + templateUrl: relationTableTemplate + }; +} + +/*@ngInject*/ +function RelationTableController($scope, $q, $mdDialog, $document, $translate, $filter, utils, types, entityRelationService) { + + let vm = this; + + vm.relations = []; + vm.relationsCount = 0; + vm.allRelations = []; + vm.selectedRelations = []; + + vm.query = { + order: 'typeName', + limit: 5, + page: 1, + search: null + }; + + vm.enterFilterMode = enterFilterMode; + vm.exitFilterMode = exitFilterMode; + vm.onReorder = onReorder; + vm.onPaginate = onPaginate; + vm.addRelation = addRelation; + vm.editRelation = editRelation; + vm.deleteRelation = deleteRelation; + vm.deleteRelations = deleteRelations; + vm.reloadRelations = reloadRelations; + vm.updateRelations = updateRelations; + + + $scope.$watch("vm.entityId", function(newVal, prevVal) { + if (newVal && !angular.equals(newVal, prevVal)) { + reloadRelations(); + } + }); + + $scope.$watch("vm.query.search", function(newVal, prevVal) { + if (!angular.equals(newVal, prevVal) && vm.query.search != null) { + updateRelations(); + } + }); + + function enterFilterMode () { + vm.query.search = ''; + } + + function exitFilterMode () { + vm.query.search = null; + updateRelations(); + } + + function onReorder () { + updateRelations(); + } + + function onPaginate () { + updateRelations(); + } + + function addRelation($event) { + if ($event) { + $event.stopPropagation(); + } + var from = { + id: vm.entityId, + entityType: vm.entityType + }; + $mdDialog.show({ + controller: AddRelationController, + controllerAs: 'vm', + templateUrl: addRelationTemplate, + parent: angular.element($document[0].body), + locals: { from: from }, + fullscreen: true, + targetEvent: $event + }).then(function () { + reloadRelations(); + }, function () { + }); + } + + function editRelation($event, /*relation*/) { + if ($event) { + $event.stopPropagation(); + } + //TODO: + } + + function deleteRelation($event, /*relation*/) { + if ($event) { + $event.stopPropagation(); + } + //TODO: + } + + function deleteRelations($event) { + if ($event) { + $event.stopPropagation(); + } + //TODO: + } + + function reloadRelations () { + vm.allRelations.length = 0; + vm.relations.length = 0; + vm.relationsPromise = entityRelationService.findInfoByFrom(vm.entityId, vm.entityType); + vm.relationsPromise.then( + function success(allRelations) { + allRelations.forEach(function(relation) { + relation.typeName = $translate.instant('relation.relation-type.' + relation.type); + relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType)); + }); + vm.allRelations = allRelations; + vm.selectedRelations = []; + vm.updateRelations(); + vm.relationsPromise = null; + }, + function fail() { + vm.allRelations = []; + vm.selectedRelations = []; + vm.updateRelations(); + vm.relationsPromise = null; + } + ) + } + + function updateRelations () { + vm.selectedRelations = []; + var result = $filter('orderBy')(vm.allRelations, vm.query.order); + if (vm.query.search != null) { + result = $filter('filter')(result, {$: vm.query.search}); + } + vm.relationsCount = result.length; + var startIndex = vm.query.limit * (vm.query.page - 1); + vm.relations = result.slice(startIndex, startIndex + vm.query.limit); + } + +} diff --git a/ui/src/app/entity/relation/relation-table.scss b/ui/src/app/entity/relation/relation-table.scss new file mode 100644 index 0000000000..f5df1fc3f6 --- /dev/null +++ b/ui/src/app/entity/relation/relation-table.scss @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../scss/constants'; + +$md-light: rgba(255, 255, 255, 100%); + +.tb-relation-table { + md-toolbar.md-table-toolbar.alternate { + .md-toolbar-tools { + md-icon { + color: $md-light; + } + } + } +} diff --git a/ui/src/app/entity/relation/relation-table.tpl.html b/ui/src/app/entity/relation/relation-table.tpl.html new file mode 100644 index 0000000000..dc0df57965 --- /dev/null +++ b/ui/src/app/entity/relation/relation-table.tpl.html @@ -0,0 +1,119 @@ + + +
+ +
+ relation.entity-relations + + + add + + {{ 'action.add' | translate }} + + + + search + + {{ 'action.search' | translate }} + + + + refresh + + {{ 'action.refresh' | translate }} + + +
+
+ +
+ + search + + {{ 'action.search' | translate }} + + + + + + + + close + + {{ 'action.close' | translate }} + + +
+
+ +
+ relation.selected-relations + + + delete + + {{ 'action.delete' | translate }} + + +
+
+ +
Last update timeKeyValueattribute.last-update-timeattribute.keyattribute.value
+ + + + + + + + + + + + + + + + +
relation.typerelation.to-entity-typerelation.to-entity-name 
{{ relation.typeName }}{{ relation.toEntityTypeName }}{{ relation.toName }} + + edit + + {{ 'relation.edit' | translate }} + + + + delete + + {{ 'relation.delete' | translate }} + + +
+ + + +
+ diff --git a/ui/src/app/locale/locale.constant-es.js b/ui/src/app/locale/locale.constant-es.js index 153019d44c..e4d4b49e53 100644 --- a/ui/src/app/locale/locale.constant-es.js +++ b/ui/src/app/locale/locale.constant-es.js @@ -235,7 +235,7 @@ "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", "select-dashboard": "Seleccionar panel", - "no-dashboards-matching": "Panel '{{dashboard}}' no encontrado.", + "no-dashboards-matching": "Panel '{{entity}}' no encontrado.", "dashboard-required": "Panel requerido.", "select-existing": "Seleccionar paneles existentes", "create-new": "Crear nuevo panel", @@ -330,7 +330,7 @@ "create-new-key": "Crear nueva clave!", "duplicate-alias-error": "Alias duplicado '{{alias}}'.
El alias de los dispositivos deben ser únicos dentro del panel.", "configure-alias": "Configurar alias '{{alias}}'", - "no-devices-matching": "No se encontró dispositivo '{{device}}'", + "no-devices-matching": "No se encontró dispositivo '{{entity}}'", "alias": "Alias", "alias-required": "Alias de dispositivo requerido.", "remove-alias": "Eliminar alias", @@ -529,7 +529,7 @@ "system": "Sistema", "select-plugin": "plugin", "plugin": "Plugin", - "no-plugins-matching": "No se encontraron plugins: '{{plugin}}'", + "no-plugins-matching": "No se encontraron plugins: '{{entity}}'", "plugin-required": "Plugin requerido.", "plugin-require-match": "Por favor, elija un plugin existente.", "events": "Eventos", diff --git a/ui/src/app/locale/locale.constant-ko.js b/ui/src/app/locale/locale.constant-ko.js index 8b2d4bacb7..32dbf49c4a 100644 --- a/ui/src/app/locale/locale.constant-ko.js +++ b/ui/src/app/locale/locale.constant-ko.js @@ -218,7 +218,7 @@ export default function addLocaleKorean(locales) { "unassign-dashboards-title": "{ count, select, 1 {대시보드 1개} other {대시보드 #개} }의 할당을 취소하시겠습니까?", "unassign-dashboards-text": "선택된 대시보드가 할당 해제되고 커스터머는 액세스 할 수 없게됩니다.", "select-dashboard": "대시보드 선택", - "no-dashboards-matching": "'{{dashboard}}'와 일치하는 대시보드가 없습니다.", + "no-dashboards-matching": "'{{entity}}'와 일치하는 대시보드가 없습니다.", "dashboard-required": "대시보드를 입력하세요.", "select-existing": "기존 대시보드 선택", "create-new": "대시보드 생성", @@ -305,7 +305,7 @@ export default function addLocaleKorean(locales) { "create-new-key": "새로 만들기!", "duplicate-alias-error": "중복된 '{{alias}}' 앨리어스가 있습니다.
디바이스 앨리어스는 대시보드 내에서 고유해야 합니다.", "configure-alias": "'{{alias}}' 앨리어스 구성", - "no-devices-matching": "'{{device}}'와 일치하는 디바이스를 찾을 수 없습니다.", + "no-devices-matching": "'{{entity}}'와 일치하는 디바이스를 찾을 수 없습니다.", "alias": "앨리어스", "alias-required": "디바이스 앨리어스를 입력하세요.", "remove-alias": "디바이스 앨리어스 삭제", @@ -496,7 +496,7 @@ export default function addLocaleKorean(locales) { "system": "시스템", "select-plugin": "플러그인 선택", "plugin": "플러그인", - "no-plugins-matching": "'{{plugin}}'과 일치하는 플러그인을 찾을 수 없습니다.", + "no-plugins-matching": "'{{entity}}'과 일치하는 플러그인을 찾을 수 없습니다.", "plugin-required": "플러그인을 입력하세요.", "plugin-require-match": "기존의 플러그인을 선택해주세요.", "events": "이벤트", diff --git a/ui/src/app/locale/locale.constant-ru.js b/ui/src/app/locale/locale.constant-ru.js index 47c9460bbd..d7734b46d7 100644 --- a/ui/src/app/locale/locale.constant-ru.js +++ b/ui/src/app/locale/locale.constant-ru.js @@ -235,7 +235,7 @@ export default function addLocaleRussian(locales) { "socialshare-text": "'{{dashboardTitle}}' сделано ThingsBoard", "socialshare-title": "'{{dashboardTitle}}' сделано ThingsBoard", "select-dashboard": "Выберите дашборд", - "no-dashboards-matching": "Дашборд '{{dashboard}}' не найден.", + "no-dashboards-matching": "Дашборд '{{entity}}' не найден.", "dashboard-required": "Дашборд обязателен.", "select-existing": "Выберите существующий дашборд", "create-new": "Создать новый дашборд", @@ -330,7 +330,7 @@ export default function addLocaleRussian(locales) { "create-new-key": "Создать новый!", "duplicate-alias-error": "Найден дублирующийся псевдоним '{{alias}}'.
В рамках дашборда псевдонимы устройств должны быть уникальными.", "configure-alias": "Конфигурировать '{{alias}}' псевдоним", - "no-devices-matching": "Устройство '{{device}}' не найдено.", + "no-devices-matching": "Устройство '{{entity}}' не найдено.", "alias": "Псевдоним", "alias-required": "Псевдоним устройства обязателен.", "remove-alias": "Удалить псевдоним устройства", @@ -529,7 +529,7 @@ export default function addLocaleRussian(locales) { "system": "Системный", "select-plugin": "Выберите плагин", "plugin": "Плагин", - "no-plugins-matching": "Плагин '{{plugin}}' не найден.", + "no-plugins-matching": "Плагин '{{entity}}' не найден.", "plugin-required": "Плагин обязателен.", "plugin-require-match": "Пожалуйста, выберите существующий плагин.", "events": "События", diff --git a/ui/src/app/locale/locale.constant-zh.js b/ui/src/app/locale/locale.constant-zh.js index ba8e3c08da..32460702ec 100644 --- a/ui/src/app/locale/locale.constant-zh.js +++ b/ui/src/app/locale/locale.constant-zh.js @@ -235,7 +235,7 @@ export default function addLocaleChinese(locales) { "socialshare-text" : "'{{dashboardTitle}}' 由ThingsBoard提供支持", "socialshare-title" : "'{{dashboardTitle}}' 由ThingsBoard提供支持", "select-dashboard" : "选择仪表板", - "no-dashboards-matching" : "找不到符合 '{{dashboard}}' 的仪表板。", + "no-dashboards-matching" : "找不到符合 '{{entity}}' 的仪表板。", "dashboard-required" : "仪表板是必需的。", "select-existing" : "选择现有仪表板", "create-new" : "创建新的仪表板", @@ -330,7 +330,7 @@ export default function addLocaleChinese(locales) { "create-new-key": "创建一个新的!", "duplicate-alias-error" : "找到重复别名 '{{alias}}'。
设备别名必须是唯一的。", "configure-alias" : "配置 '{{alias}}' 别名", - "no-devices-matching" : "找不到与 '{{device}}' 匹配的设备。", + "no-devices-matching" : "找不到与 '{{entity}}' 匹配的设备。", "alias" : "别名", "alias-required" : "需要设备别名。", "remove-alias": "删除设备别名", @@ -529,7 +529,7 @@ export default function addLocaleChinese(locales) { "system" : "系统", "select-plugin" : "选择插件", "plugin" : "插件", - "no-plugins-matching" : "没有找到匹配'{{plugin}}'的插件。", + "no-plugins-matching" : "没有找到匹配'{{entity}}'的插件。", "plugin-required" : "插件是必需的。", "plugin-require-match" : "请选择一个现有的插件。", "events" : "事件", diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index 50f6d8f5c7..d25db5da32 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -106,6 +106,12 @@ export default angular.module('thingsboard.locale', []) "enable-tls": "Enable TLS", "send-test-mail": "Send test mail" }, + "alarm": { + "alarm": "Alarm", + "select-alarm": "Select alarm", + "no-alarms-matching": "No alarms matching '{{entity}}' were found.", + "alarm-required": "Alarm is required" + }, "asset": { "asset": "Asset", "assets": "Assets", @@ -157,7 +163,10 @@ export default angular.module('thingsboard.locale', []) "unassign-assets-title": "Are you sure you want to unassign { count, select, 1 {1 asset} other {# assets} }?", "unassign-assets-text": "After the confirmation all selected assets will be unassigned and won't be accessible by the customer.", "copyId": "Copy asset Id", - "idCopiedMessage": "Asset Id has been copied to clipboard" + "idCopiedMessage": "Asset Id has been copied to clipboard", + "select-asset": "Select asset", + "no-assets-matching": "No assets matching '{{entity}}' were found.", + "asset-required": "Asset is required" }, "attribute": { "attributes": "Attributes", @@ -169,6 +178,7 @@ export default angular.module('thingsboard.locale', []) "scope-shared": "Shared attributes", "add": "Add attribute", "key": "Key", + "last-update-time": "Last update time", "key-required": "Attribute key is required.", "value": "Value", "value-required": "Attribute value is required.", @@ -210,6 +220,7 @@ export default angular.module('thingsboard.locale', []) "enter-search": "Enter search" }, "customer": { + "customer": "Customer", "customers": "Customers", "management": "Customer management", "dashboard": "Customer Dashboard", @@ -246,7 +257,10 @@ export default angular.module('thingsboard.locale', []) "details": "Details", "events": "Events", "copyId": "Copy customer Id", - "idCopiedMessage": "Customer Id has been copied to clipboard" + "idCopiedMessage": "Customer Id has been copied to clipboard", + "select-customer": "Select customer", + "no-customers-matching": "No customers matching '{{entity}}' were found.", + "customer-required": "Customer is required" }, "datetime": { "date-from": "Date from", @@ -304,7 +318,7 @@ export default angular.module('thingsboard.locale', []) "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", "select-dashboard": "Select dashboard", - "no-dashboards-matching": "No dashboards matching '{{dashboard}}' were found.", + "no-dashboards-matching": "No dashboards matching '{{entity}}' were found.", "dashboard-required": "Dashboard is required.", "select-existing": "Select existing dashboard", "create-new": "Create new dashboard", @@ -425,7 +439,7 @@ export default angular.module('thingsboard.locale', []) "create-new-key": "Create a new one!", "duplicate-alias-error": "Duplicate alias found '{{alias}}'.
Device aliases must be unique whithin the dashboard.", "configure-alias": "Configure '{{alias}}' alias", - "no-devices-matching": "No devices matching '{{device}}' were found.", + "no-devices-matching": "No devices matching '{{entity}}' were found.", "alias": "Alias", "alias-required": "Device alias is required.", "remove-alias": "Remove device alias", @@ -497,7 +511,8 @@ export default angular.module('thingsboard.locale', []) "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):
{{widgetsList}}", "is-gateway": "Is gateway", "public": "Public", - "device-public": "Device is public" + "device-public": "Device is public", + "select-device": "Select device" }, "dialog": { "close": "Close dialog" @@ -535,6 +550,9 @@ export default angular.module('thingsboard.locale', []) "type-plugin": "Plugin", "type-tenant": "Tenant", "type-customer": "Customer", + "type-user": "User", + "type-dashboard": "Dashboard", + "type-alarm": "Alarm", "select-entities": "Select entities", "no-aliases-found": "No aliases found.", "no-alias-matching": "'{{alias}}' not found.", @@ -672,7 +690,7 @@ export default angular.module('thingsboard.locale', []) "system": "System", "select-plugin": "Select plugin", "plugin": "Plugin", - "no-plugins-matching": "No plugins matching '{{plugin}}' were found.", + "no-plugins-matching": "No plugins matching '{{entity}}' were found.", "plugin-required": "Plugin is required.", "plugin-require-match": "Please select an existing plugin.", "events": "Events", @@ -685,6 +703,7 @@ export default angular.module('thingsboard.locale', []) "invalid-plugin-file-error": "Unable to import plugin: Invalid plugin data structure.", "copyId": "Copy plugin Id", "idCopiedMessage": "Plugin Id has been copied to clipboard" + }, "position": { "top": "Top", @@ -697,7 +716,24 @@ export default angular.module('thingsboard.locale', []) "change-password": "Change Password", "current-password": "Current password" }, + "relation": { + "relations": "Relations", + "entity-relations": "Entity relations", + "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected", + "type": "Type", + "to-entity-type": "Entity type", + "to-entity-name": "Entity name", + "edit": "Edit relation", + "delete": "Delete relation", + "relation-type": "Relation type", + "relation-types": { + "Contains": "Contains", + "Manages": "Manages" + }, + "add": "Add relation" + }, "rule": { + "rule": "Rule", "rules": "Rules", "delete": "Delete rule", "activate": "Activate rule", @@ -749,12 +785,16 @@ export default angular.module('thingsboard.locale', []) "rule-file": "Rule file", "invalid-rule-file-error": "Unable to import rule: Invalid rule data structure.", "copyId": "Copy rule Id", - "idCopiedMessage": "Rule Id has been copied to clipboard" + "idCopiedMessage": "Rule Id has been copied to clipboard", + "select-rule": "Select rule", + "no-rules-matching": "No rules matching '{{entity}}' were found.", + "rule-required": "Rule is required" }, "rule-plugin": { "management": "Rules and plugins management" }, "tenant": { + "tenant": "Tenant", "tenants": "Tenants", "management": "Tenant management", "add": "Add Tenant", @@ -775,7 +815,10 @@ export default angular.module('thingsboard.locale', []) "details": "Details", "events": "Events", "copyId": "Copy tenant Id", - "idCopiedMessage": "Tenant Id has been copied to clipboard" + "idCopiedMessage": "Tenant Id has been copied to clipboard", + "select-tenant": "Select tenant", + "no-tenants-matching": "No tenants matching '{{entity}}' were found.", + "tenant-required": "Tenant is required" }, "timeinterval": { "seconds-interval": "{ seconds, select, 1 {1 second} other {# seconds} }", @@ -803,6 +846,7 @@ export default angular.module('thingsboard.locale', []) "time-period": "Time period" }, "user": { + "user": "User", "users": "Users", "customer-users": "Customer Users", "tenant-admins": "Tenant Admins", @@ -828,7 +872,10 @@ export default angular.module('thingsboard.locale', []) "last-name": "Last Name", "description": "Description", "default-dashboard": "Default dashboard", - "always-fullscreen": "Always fullscreen" + "always-fullscreen": "Always fullscreen", + "select-user": "Select user", + "no-users-matching": "No users matching '{{entity}}' were found.", + "user-required": "User is required" }, "value": { "type": "Value type", diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss index 4ffabc975e..c062d4ea83 100644 --- a/ui/src/scss/main.scss +++ b/ui/src/scss/main.scss @@ -261,6 +261,45 @@ pre.tb-highlight { font-size: 16px; } +.tb-data-table { + md-toolbar { + z-index: 0; + } + span.no-data-found { + position: relative; + height: calc(100% - 57px); + text-transform: uppercase; + display: flex; + } + table.md-table { + tbody { + tr { + td { + &.tb-action-cell { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 72px; + max-width: 72px; + width: 72px; + .md-button { + &.md-icon-button { + margin: 0; + padding: 6px; + width: 36px; + height: 36px; + } + } + .tb-spacer { + padding-left: 38px; + } + } + } + } + } + } +} + /*********************** * Flow From 3c43d039e8970d6ae1ebdfc21b41bd2cec7daba4 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 29 May 2017 17:50:33 +0300 Subject: [PATCH 36/37] Add relationTypeGroup to relation column family. --- .../controller/EntityRelationController.java | 67 +++++++++++---- .../server/common/data/Customer.java | 3 + .../server/common/data/DashboardInfo.java | 2 + .../server/common/data/Tenant.java | 2 + .../thingsboard/server/common/data/User.java | 2 + .../server/common/data/alarm/Alarm.java | 3 +- .../common/data/relation/EntityRelation.java | 37 +++++++-- .../data/relation/RelationTypeGroup.java | 23 ++++++ .../server/dao/alarm/AlarmDaoImpl.java | 3 +- .../server/dao/alarm/BaseAlarmService.java | 17 ++-- .../server/dao/model/ModelConstants.java | 1 + .../model/type/RelationTypeGroupCodec.java | 28 +++++++ .../server/dao/relation/BaseRelationDao.java | 69 +++++++++++----- .../dao/relation/BaseRelationService.java | 82 +++++++++++-------- .../server/dao/relation/RelationDao.java | 15 ++-- .../server/dao/relation/RelationService.java | 15 ++-- dao/src/main/resources/schema.cql | 17 ++-- .../dao/service/RelationServiceImplTest.java | 43 +++++----- .../dao/service/RelationServiceTest.java | 43 +++++----- pom.xml | 2 +- ui/src/app/entity/entity-select.directive.js | 6 +- ui/src/app/entity/entity-select.tpl.html | 2 +- .../entity/entity-subtype-select.directive.js | 2 +- 23 files changed, 329 insertions(+), 155 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/type/RelationTypeGroupCodec.java diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 04f1c3ffc7..1a08d56a7f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationInfo; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.relation.EntityRelationsQuery; import org.thingsboard.server.exception.ThingsboardErrorCode; import org.thingsboard.server.exception.ThingsboardException; @@ -51,19 +52,23 @@ public class EntityRelationController extends BaseController { @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {"fromId", "fromType", "relationType", "toId", "toType"}) @ResponseStatus(value = HttpStatus.OK) public void deleteRelation(@RequestParam("fromId") String strFromId, - @RequestParam("fromType") String strFromType, @RequestParam("relationType") String strRelationType, + @RequestParam("fromType") String strFromType, + @RequestParam("relationType") String strRelationType, + @RequestParam("relationTypeGroup") String strRelationTypeGroup, @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException { checkParameter("fromId", strFromId); checkParameter("fromType", strFromType); checkParameter("relationType", strRelationType); + checkParameter("relationTypeGroup", strRelationTypeGroup); checkParameter("toId", strToId); checkParameter("toType", strToType); EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); checkEntityId(fromId); checkEntityId(toId); + RelationTypeGroup relationTypeGroup = RelationTypeGroup.valueOf(strRelationTypeGroup); try { - Boolean found = relationService.deleteRelation(fromId, toId, strRelationType).get(); + Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup).get(); if (!found) { throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); } @@ -92,7 +97,9 @@ public class EntityRelationController extends BaseController { @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType", "toId", "toType"}) @ResponseStatus(value = HttpStatus.OK) public void checkRelation(@RequestParam("fromId") String strFromId, - @RequestParam("fromType") String strFromType, @RequestParam("relationType") String strRelationType, + @RequestParam("fromType") String strFromType, + @RequestParam("relationType") String strRelationType, + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup, @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException { try { checkParameter("fromId", strFromId); @@ -104,7 +111,8 @@ public class EntityRelationController extends BaseController { EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); checkEntityId(fromId); checkEntityId(toId); - Boolean found = relationService.checkRelation(fromId, toId, strRelationType).get(); + RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); + Boolean found = relationService.checkRelation(fromId, toId, strRelationType, typeGroup).get(); if (!found) { throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); } @@ -116,13 +124,16 @@ public class EntityRelationController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType"}) @ResponseBody - public List findByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType) throws ThingsboardException { + public List findByFrom(@RequestParam("fromId") String strFromId, + @RequestParam("fromType") String strFromType, + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { checkParameter("fromId", strFromId); checkParameter("fromType", strFromType); EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); checkEntityId(entityId); + RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); try { - return checkNotNull(relationService.findByFrom(entityId).get()); + return checkNotNull(relationService.findByFrom(entityId, typeGroup).get()); } catch (Exception e) { throw handleException(e); } @@ -131,13 +142,16 @@ public class EntityRelationController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"fromId", "fromType"}) @ResponseBody - public List findInfoByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType) throws ThingsboardException { + public List findInfoByFrom(@RequestParam("fromId") String strFromId, + @RequestParam("fromType") String strFromType, + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { checkParameter("fromId", strFromId); checkParameter("fromType", strFromType); EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); checkEntityId(entityId); + RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); try { - return checkNotNull(relationService.findInfoByFrom(entityId).get()); + return checkNotNull(relationService.findInfoByFrom(entityId, typeGroup).get()); } catch (Exception e) { throw handleException(e); } @@ -146,15 +160,18 @@ public class EntityRelationController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"}) @ResponseBody - public List findByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType - , @RequestParam("relationType") String strRelationType) throws ThingsboardException { + public List findByFrom(@RequestParam("fromId") String strFromId, + @RequestParam("fromType") String strFromType, + @RequestParam("relationType") String strRelationType, + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { checkParameter("fromId", strFromId); checkParameter("fromType", strFromType); checkParameter("relationType", strRelationType); EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); checkEntityId(entityId); + RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); try { - return checkNotNull(relationService.findByFromAndType(entityId, strRelationType).get()); + return checkNotNull(relationService.findByFromAndType(entityId, strRelationType, typeGroup).get()); } catch (Exception e) { throw handleException(e); } @@ -163,13 +180,16 @@ public class EntityRelationController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType"}) @ResponseBody - public List findByTo(@RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException { + public List findByTo(@RequestParam("toId") String strToId, + @RequestParam("toType") String strToType, + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { checkParameter("toId", strToId); checkParameter("toType", strToType); EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); checkEntityId(entityId); + RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); try { - return checkNotNull(relationService.findByTo(entityId).get()); + return checkNotNull(relationService.findByTo(entityId, typeGroup).get()); } catch (Exception e) { throw handleException(e); } @@ -178,15 +198,18 @@ public class EntityRelationController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType", "relationType"}) @ResponseBody - public List findByTo(@RequestParam("toId") String strToId, @RequestParam("toType") String strToType - , @RequestParam("relationType") String strRelationType) throws ThingsboardException { + public List findByTo(@RequestParam("toId") String strToId, + @RequestParam("toType") String strToType, + @RequestParam("relationType") String strRelationType, + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { checkParameter("toId", strToId); checkParameter("toType", strToType); checkParameter("relationType", strRelationType); EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); checkEntityId(entityId); + RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); try { - return checkNotNull(relationService.findByToAndType(entityId, strRelationType).get()); + return checkNotNull(relationService.findByToAndType(entityId, strRelationType, typeGroup).get()); } catch (Exception e) { throw handleException(e); } @@ -207,4 +230,16 @@ public class EntityRelationController extends BaseController { } } + private RelationTypeGroup parseRelationTypeGroup(String strRelationTypeGroup, RelationTypeGroup defaultValue) { + RelationTypeGroup result = defaultValue; + if (strRelationTypeGroup != null && strRelationTypeGroup.trim().length()>0) { + try { + result = RelationTypeGroup.valueOf(strRelationTypeGroup); + } catch (IllegalArgumentException e) { + result = defaultValue; + } + } + return result; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index f8c1e3045a..ec535bf44c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -60,6 +62,7 @@ public class Customer extends ContactBased implements HasName { } @Override + @JsonProperty(access = Access.READ_ONLY) public String getName() { return title; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index c4daf9d85c..3be3b79d56 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data; +import com.fasterxml.jackson.annotation.JsonProperty; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; @@ -65,6 +66,7 @@ public class DashboardInfo extends SearchTextBased implements HasNa } @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { return title; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java index 3a5f6bf09a..4501759e55 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data; +import com.fasterxml.jackson.annotation.JsonProperty; import org.thingsboard.server.common.data.id.TenantId; import com.fasterxml.jackson.databind.JsonNode; @@ -51,6 +52,7 @@ public class Tenant extends ContactBased implements HasName { } @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { return title; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index 0543ef0a38..1b97e2e49f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data; +import com.fasterxml.jackson.annotation.JsonProperty; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; @@ -78,6 +79,7 @@ public class User extends SearchTextBased implements HasName { } @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { return email; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index 7bf7a08bd9..09695e5ee8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.common.data.alarm; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -54,6 +54,7 @@ public class Alarm extends BaseData implements HasName { } @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { return type; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java index 39e7d54978..8dab589596 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java @@ -30,6 +30,7 @@ public class EntityRelation { private EntityId from; private EntityId to; private String type; + private RelationTypeGroup typeGroup; private JsonNode additionalInfo; public EntityRelation() { @@ -37,13 +38,18 @@ public class EntityRelation { } public EntityRelation(EntityId from, EntityId to, String type) { - this(from, to, type, null); + this(from, to, type, RelationTypeGroup.COMMON); } - public EntityRelation(EntityId from, EntityId to, String type, JsonNode additionalInfo) { + public EntityRelation(EntityId from, EntityId to, String type, RelationTypeGroup typeGroup) { + this(from, to, type, typeGroup, null); + } + + public EntityRelation(EntityId from, EntityId to, String type, RelationTypeGroup typeGroup, JsonNode additionalInfo) { this.from = from; this.to = to; this.type = type; + this.typeGroup = typeGroup; this.additionalInfo = additionalInfo; } @@ -51,6 +57,7 @@ public class EntityRelation { this.from = entityRelation.getFrom(); this.to = entityRelation.getTo(); this.type = entityRelation.getType(); + this.typeGroup = entityRelation.getTypeGroup(); this.additionalInfo = entityRelation.getAdditionalInfo(); } @@ -78,6 +85,14 @@ public class EntityRelation { this.type = type; } + public RelationTypeGroup getTypeGroup() { + return typeGroup; + } + + public void setTypeGroup(RelationTypeGroup typeGroup) { + this.typeGroup = typeGroup; + } + public JsonNode getAdditionalInfo() { return additionalInfo; } @@ -90,14 +105,22 @@ public class EntityRelation { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - EntityRelation relation = (EntityRelation) o; - return Objects.equals(from, relation.from) && - Objects.equals(to, relation.to) && - Objects.equals(type, relation.type); + + EntityRelation that = (EntityRelation) o; + + if (from != null ? !from.equals(that.from) : that.from != null) return false; + if (to != null ? !to.equals(that.to) : that.to != null) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + return typeGroup == that.typeGroup; + } @Override public int hashCode() { - return Objects.hash(from, to, type); + int result = from != null ? from.hashCode() : 0; + result = 31 * result + (to != null ? to.hashCode() : 0); + result = 31 * result + (type != null ? type.hashCode() : 0); + result = 31 * result + (typeGroup != null ? typeGroup.hashCode() : 0); + return result; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java new file mode 100644 index 0000000000..82798ab792 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.relation; + +public enum RelationTypeGroup { + + COMMON, + ALARM + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDaoImpl.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDaoImpl.java index 994fe8036a..1362e6b3a9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDaoImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDaoImpl.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.AbstractModelDao; import org.thingsboard.server.dao.AbstractSearchTimeDao; import org.thingsboard.server.dao.model.AlarmEntity; @@ -97,7 +98,7 @@ public class AlarmDaoImpl extends AbstractModelDao implements Alarm log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink()); EntityId affectedEntity = query.getAffectedEntityId(); String relationType = query.getStatus() == null ? BaseAlarmService.ALARM_RELATION : BaseAlarmService.ALARM_RELATION_PREFIX + query.getStatus().name(); - ListenableFuture> relations = relationDao.findRelations(affectedEntity, relationType, EntityType.ALARM, query.getPageLink()); + ListenableFuture> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink()); return Futures.transform(relations, (AsyncFunction, List>) input -> { List> alarmFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 5b31a51f22..3981f64701 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.BaseEntityService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -115,11 +116,11 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE)); List parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList()); for (EntityId parentId : parentEntities) { - createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION)); - createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name())); + createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION, RelationTypeGroup.ALARM)); + createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name(), RelationTypeGroup.ALARM)); } - createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION)); - createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name())); + createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION, RelationTypeGroup.ALARM)); + createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name(), RelationTypeGroup.ALARM)); return saved; } @@ -244,11 +245,11 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE)); List parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList()); for (EntityId parentId : parentEntities) { - deleteRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name())); - createRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name())); + deleteRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name(), RelationTypeGroup.ALARM)); + createRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name(), RelationTypeGroup.ALARM)); } - deleteRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name())); - createRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name())); + deleteRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name(), RelationTypeGroup.ALARM)); + createRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name(), RelationTypeGroup.ALARM)); } catch (ExecutionException | InterruptedException e) { log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus); throw new RuntimeException(e); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 967f4d1122..dfad10b05d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -175,6 +175,7 @@ public class ModelConstants { public static final String RELATION_TO_ID_PROPERTY = "to_id"; public static final String RELATION_TO_TYPE_PROPERTY = "to_type"; public static final String RELATION_TYPE_PROPERTY = "relation_type"; + public static final String RELATION_TYPE_GROUP_PROPERTY = "relation_type_group"; public static final String RELATION_BY_TYPE_AND_CHILD_TYPE_VIEW_NAME = "relation_by_type_and_child_type"; public static final String RELATION_REVERSE_VIEW_NAME = "reverse_relation"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/type/RelationTypeGroupCodec.java b/dao/src/main/java/org/thingsboard/server/dao/model/type/RelationTypeGroupCodec.java new file mode 100644 index 0000000000..1c0514e2f1 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/type/RelationTypeGroupCodec.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.type; + +import org.thingsboard.server.common.data.relation.RelationTypeGroup; + +import com.datastax.driver.extras.codecs.enums.EnumNameCodec; + +public class RelationTypeGroupCodec extends EnumNameCodec { + + public RelationTypeGroupCodec() { + super(RelationTypeGroup.class); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java index 1b05866c75..9fc2382d8d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java @@ -29,9 +29,11 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.AbstractAsyncDao; import org.thingsboard.server.dao.AbstractSearchTimeDao; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.model.type.RelationTypeGroupCodec; import javax.annotation.Nullable; import javax.annotation.PostConstruct; @@ -54,12 +56,15 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { ModelConstants.RELATION_FROM_TYPE_PROPERTY + "," + ModelConstants.RELATION_TO_ID_PROPERTY + "," + ModelConstants.RELATION_TO_TYPE_PROPERTY + "," + + ModelConstants.RELATION_TYPE_GROUP_PROPERTY + "," + ModelConstants.RELATION_TYPE_PROPERTY + "," + ModelConstants.ADDITIONAL_INFO_PROPERTY; public static final String FROM = " FROM "; public static final String WHERE = " WHERE "; public static final String AND = " AND "; + private static final RelationTypeGroupCodec relationTypeGroupCodec = new RelationTypeGroupCodec(); + private PreparedStatement saveStmt; private PreparedStatement findAllByFromStmt; private PreparedStatement findAllByFromAndTypeStmt; @@ -75,43 +80,52 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { } @Override - public ListenableFuture> findAllByFrom(EntityId from) { - BoundStatement stmt = getFindAllByFromStmt().bind().setUUID(0, from.getId()).setString(1, from.getEntityType().name()); + public ListenableFuture> findAllByFrom(EntityId from, RelationTypeGroup typeGroup) { + BoundStatement stmt = getFindAllByFromStmt().bind() + .setUUID(0, from.getId()) + .setString(1, from.getEntityType().name()) + .set(2, typeGroup, relationTypeGroupCodec); return executeAsyncRead(from, stmt); } @Override - public ListenableFuture> findAllByFromAndType(EntityId from, String relationType) { + public ListenableFuture> findAllByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { BoundStatement stmt = getFindAllByFromAndTypeStmt().bind() .setUUID(0, from.getId()) .setString(1, from.getEntityType().name()) - .setString(2, relationType); + .set(2, typeGroup, relationTypeGroupCodec) + .setString(3, relationType); return executeAsyncRead(from, stmt); } @Override - public ListenableFuture> findAllByTo(EntityId to) { - BoundStatement stmt = getFindAllByToStmt().bind().setUUID(0, to.getId()).setString(1, to.getEntityType().name()); + public ListenableFuture> findAllByTo(EntityId to, RelationTypeGroup typeGroup) { + BoundStatement stmt = getFindAllByToStmt().bind() + .setUUID(0, to.getId()) + .setString(1, to.getEntityType().name()) + .set(2, typeGroup, relationTypeGroupCodec); return executeAsyncRead(to, stmt); } @Override - public ListenableFuture> findAllByToAndType(EntityId to, String relationType) { + public ListenableFuture> findAllByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { BoundStatement stmt = getFindAllByToAndTypeStmt().bind() .setUUID(0, to.getId()) .setString(1, to.getEntityType().name()) - .setString(2, relationType); + .set(2, typeGroup, relationTypeGroupCodec) + .setString(3, relationType); return executeAsyncRead(to, stmt); } @Override - public ListenableFuture checkRelation(EntityId from, EntityId to, String relationType) { + public ListenableFuture checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { BoundStatement stmt = getCheckRelationStmt().bind() .setUUID(0, from.getId()) .setString(1, from.getEntityType().name()) .setUUID(2, to.getId()) .setString(3, to.getEntityType().name()) - .setString(4, relationType); + .set(4, typeGroup, relationTypeGroupCodec) + .setString(5, relationType); return getFuture(executeAsyncRead(stmt), rs -> rs != null ? rs.one() != null : false); } @@ -122,25 +136,27 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { .setString(1, relation.getFrom().getEntityType().name()) .setUUID(2, relation.getTo().getId()) .setString(3, relation.getTo().getEntityType().name()) - .setString(4, relation.getType()) - .set(5, relation.getAdditionalInfo(), JsonNode.class); + .set(4, relation.getTypeGroup(), relationTypeGroupCodec) + .setString(5, relation.getType()) + .set(6, relation.getAdditionalInfo(), JsonNode.class); ResultSetFuture future = executeAsyncWrite(stmt); return getBooleanListenableFuture(future); } @Override public ListenableFuture deleteRelation(EntityRelation relation) { - return deleteRelation(relation.getFrom(), relation.getTo(), relation.getType()); + return deleteRelation(relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup()); } @Override - public ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType) { + public ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { BoundStatement stmt = getDeleteStmt().bind() .setUUID(0, from.getId()) .setString(1, from.getEntityType().name()) .setUUID(2, to.getId()) .setString(3, to.getEntityType().name()) - .setString(4, relationType); + .set(4, typeGroup, relationTypeGroupCodec) + .setString(5, relationType); ResultSetFuture future = executeAsyncWrite(stmt); return getBooleanListenableFuture(future); } @@ -155,13 +171,16 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { } @Override - public ListenableFuture> findRelations(EntityId from, String relationType, EntityType childType, TimePageLink pageLink) { + public ListenableFuture> findRelations(EntityId from, String relationType, RelationTypeGroup typeGroup, EntityType childType, TimePageLink pageLink) { Select.Where query = AbstractSearchTimeDao.buildQuery(ModelConstants.RELATION_BY_TYPE_AND_CHILD_TYPE_VIEW_NAME, Arrays.asList(eq(ModelConstants.RELATION_FROM_ID_PROPERTY, from.getId()), eq(ModelConstants.RELATION_FROM_TYPE_PROPERTY, from.getEntityType().name()), + eq(ModelConstants.RELATION_TYPE_GROUP_PROPERTY, typeGroup.name()), eq(ModelConstants.RELATION_TYPE_PROPERTY, relationType), eq(ModelConstants.RELATION_TO_TYPE_PROPERTY, childType.name())), - Arrays.asList(QueryBuilder.asc(ModelConstants.RELATION_TYPE_PROPERTY), QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY)), + Arrays.asList(QueryBuilder.asc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY), + QueryBuilder.asc(ModelConstants.RELATION_TYPE_PROPERTY), + QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY)), pageLink, ModelConstants.RELATION_TO_ID_PROPERTY); return getFuture(executeAsyncRead(query), rs -> getEntityRelations(rs)); } @@ -173,9 +192,10 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { "," + ModelConstants.RELATION_FROM_TYPE_PROPERTY + "," + ModelConstants.RELATION_TO_ID_PROPERTY + "," + ModelConstants.RELATION_TO_TYPE_PROPERTY + + "," + ModelConstants.RELATION_TYPE_GROUP_PROPERTY + "," + ModelConstants.RELATION_TYPE_PROPERTY + "," + ModelConstants.ADDITIONAL_INFO_PROPERTY + ")" + - " VALUES(?, ?, ?, ?, ?, ?)"); + " VALUES(?, ?, ?, ?, ?, ?, ?)"); } return saveStmt; } @@ -187,6 +207,7 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ?" + AND + ModelConstants.RELATION_TO_ID_PROPERTY + " = ?" + AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ?" + + AND + ModelConstants.RELATION_TYPE_GROUP_PROPERTY + " = ?" + AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ?"); } return deleteStmt; @@ -206,7 +227,8 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { findAllByFromStmt = getSession().prepare(SELECT_COLUMNS + " " + FROM + ModelConstants.RELATION_COLUMN_FAMILY_NAME + " " + WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ? " + - AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ? "); + AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ? " + + AND + ModelConstants.RELATION_TYPE_GROUP_PROPERTY + " = ? "); } return findAllByFromStmt; } @@ -217,17 +239,20 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { FROM + ModelConstants.RELATION_COLUMN_FAMILY_NAME + " " + WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ? " + AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ? " + + AND + ModelConstants.RELATION_TYPE_GROUP_PROPERTY + " = ? " + AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ? "); } return findAllByFromAndTypeStmt; } + private PreparedStatement getFindAllByToStmt() { if (findAllByToStmt == null) { findAllByToStmt = getSession().prepare(SELECT_COLUMNS + " " + FROM + ModelConstants.RELATION_REVERSE_VIEW_NAME + " " + WHERE + ModelConstants.RELATION_TO_ID_PROPERTY + " = ? " + - AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ? "); + AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ? " + + AND + ModelConstants.RELATION_TYPE_GROUP_PROPERTY + " = ? "); } return findAllByToStmt; } @@ -238,11 +263,13 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { FROM + ModelConstants.RELATION_REVERSE_VIEW_NAME + " " + WHERE + ModelConstants.RELATION_TO_ID_PROPERTY + " = ? " + AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ? " + + AND + ModelConstants.RELATION_TYPE_GROUP_PROPERTY + " = ? " + AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ? "); } return findAllByToAndTypeStmt; } + private PreparedStatement getCheckRelationStmt() { if (checkRelationStmt == null) { checkRelationStmt = getSession().prepare(SELECT_COLUMNS + " " + @@ -251,6 +278,7 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ? " + AND + ModelConstants.RELATION_TO_ID_PROPERTY + " = ? " + AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ? " + + AND + ModelConstants.RELATION_TYPE_GROUP_PROPERTY + " = ? " + AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ? "); } return checkRelationStmt; @@ -292,6 +320,7 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { private EntityRelation getEntityRelation(Row row) { EntityRelation relation = new EntityRelation(); + relation.setTypeGroup(row.get(ModelConstants.RELATION_TYPE_GROUP_PROPERTY, relationTypeGroupCodec)); relation.setType(row.getString(ModelConstants.RELATION_TYPE_PROPERTY)); relation.setAdditionalInfo(row.get(ModelConstants.ADDITIONAL_INFO_PROPERTY, JsonNode.class)); relation.setFrom(toEntity(row, ModelConstants.RELATION_FROM_ID_PROPERTY, ModelConstants.RELATION_FROM_TYPE_PROPERTY)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java index 9b4f90605d..9559cd3432 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationInfo; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.device.DeviceService; @@ -60,10 +61,10 @@ public class BaseRelationService implements RelationService { private EntityService entityService; @Override - public ListenableFuture checkRelation(EntityId from, EntityId to, String relationType) { - log.trace("Executing checkRelation [{}][{}][{}]", from, to, relationType); - validate(from, to, relationType); - return relationDao.checkRelation(from, to, relationType); + public ListenableFuture checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { + log.trace("Executing checkRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); + validate(from, to, relationType, typeGroup); + return relationDao.checkRelation(from, to, relationType, typeGroup); } @Override @@ -81,23 +82,28 @@ public class BaseRelationService implements RelationService { } @Override - public ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType) { - log.trace("Executing deleteRelation [{}][{}][{}]", from, to, relationType); - validate(from, to, relationType); - return relationDao.deleteRelation(from, to, relationType); + public ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { + log.trace("Executing deleteRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); + validate(from, to, relationType, typeGroup); + return relationDao.deleteRelation(from, to, relationType, typeGroup); } @Override public ListenableFuture deleteEntityRelations(EntityId entity) { log.trace("Executing deleteEntityRelations [{}]", entity); validate(entity); - ListenableFuture> inboundRelations = relationDao.findAllByTo(entity); - ListenableFuture> inboundDeletions = Futures.transform(inboundRelations, new AsyncFunction, List>() { + List>> inboundRelationsList = new ArrayList<>(); + for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { + inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); + } + Futures.allAsList(inboundRelationsList); + ListenableFuture>> inboundRelations = Futures.allAsList(inboundRelationsList); + ListenableFuture> inboundDeletions = Futures.transform(inboundRelations, new AsyncFunction>, List>() { @Override - public ListenableFuture> apply(List relations) throws Exception { + public ListenableFuture> apply(List> relations) throws Exception { List> results = new ArrayList<>(); - for (EntityRelation relation : relations) { - results.add(relationDao.deleteRelation(relation)); + for (List relationList : relations) { + relationList.stream().forEach(relation -> results.add(relationDao.deleteRelation(relation))); } return Futures.allAsList(results); } @@ -111,17 +117,19 @@ public class BaseRelationService implements RelationService { } @Override - public ListenableFuture> findByFrom(EntityId from) { - log.trace("Executing findByFrom [{}]", from); + public ListenableFuture> findByFrom(EntityId from, RelationTypeGroup typeGroup) { + log.trace("Executing findByFrom [{}][{}]", from, typeGroup); validate(from); - return relationDao.findAllByFrom(from); + validateTypeGroup(typeGroup); + return relationDao.findAllByFrom(from, typeGroup); } @Override - public ListenableFuture> findInfoByFrom(EntityId from) { - log.trace("Executing findInfoByFrom [{}]", from); + public ListenableFuture> findInfoByFrom(EntityId from, RelationTypeGroup typeGroup) { + log.trace("Executing findInfoByFrom [{}][{}]", from, typeGroup); validate(from); - ListenableFuture> relations = relationDao.findAllByFrom(from); + validateTypeGroup(typeGroup); + ListenableFuture> relations = relationDao.findAllByFrom(from, typeGroup); ListenableFuture> relationsInfo = Futures.transform(relations, (AsyncFunction, List>) relations1 -> { List> futures = new ArrayList<>(); @@ -143,26 +151,29 @@ public class BaseRelationService implements RelationService { } @Override - public ListenableFuture> findByFromAndType(EntityId from, String relationType) { - log.trace("Executing findByFromAndType [{}][{}]", from, relationType); + public ListenableFuture> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { + log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup); validate(from); validateType(relationType); - return relationDao.findAllByFromAndType(from, relationType); + validateTypeGroup(typeGroup); + return relationDao.findAllByFromAndType(from, relationType, typeGroup); } @Override - public ListenableFuture> findByTo(EntityId to) { - log.trace("Executing findByTo [{}]", to); + public ListenableFuture> findByTo(EntityId to, RelationTypeGroup typeGroup) { + log.trace("Executing findByTo [{}][{}]", to, typeGroup); validate(to); - return relationDao.findAllByTo(to); + validateTypeGroup(typeGroup); + return relationDao.findAllByTo(to, typeGroup); } @Override - public ListenableFuture> findByToAndType(EntityId to, String relationType) { - log.trace("Executing findByToAndType [{}][{}]", to, relationType); + public ListenableFuture> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { + log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup); validate(to); validateType(relationType); - return relationDao.findAllByToAndType(to, relationType); + validateTypeGroup(typeGroup); + return relationDao.findAllByToAndType(to, relationType, typeGroup); } @Override @@ -204,11 +215,12 @@ public class BaseRelationService implements RelationService { if (relation == null) { throw new DataValidationException("Relation type should be specified!"); } - validate(relation.getFrom(), relation.getTo(), relation.getType()); + validate(relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup()); } - protected void validate(EntityId from, EntityId to, String type) { + protected void validate(EntityId from, EntityId to, String type, RelationTypeGroup typeGroup) { validateType(type); + validateTypeGroup(typeGroup); if (from == null) { throw new DataValidationException("Relation should contain from entity!"); } @@ -223,6 +235,12 @@ public class BaseRelationService implements RelationService { } } + private void validateTypeGroup(RelationTypeGroup typeGroup) { + if (typeGroup == null) { + throw new DataValidationException("Relation type group should be specified!"); + } + } + protected void validate(EntityId entity) { if (entity == null) { throw new DataValidationException("Entity should be specified!"); @@ -294,9 +312,9 @@ public class BaseRelationService implements RelationService { private ListenableFuture> findRelations(final EntityId rootId, final EntitySearchDirection direction) { ListenableFuture> relations; if (direction == EntitySearchDirection.FROM) { - relations = findByFrom(rootId); + relations = findByFrom(rootId, RelationTypeGroup.COMMON); } else { - relations = findByTo(rootId); + relations = findByTo(rootId, RelationTypeGroup.COMMON); } return relations; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java index bec03208ac..3abadb8253 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import java.util.List; @@ -28,24 +29,24 @@ import java.util.List; */ public interface RelationDao { - ListenableFuture> findAllByFrom(EntityId from); + ListenableFuture> findAllByFrom(EntityId from, RelationTypeGroup typeGroup); - ListenableFuture> findAllByFromAndType(EntityId from, String relationType); + ListenableFuture> findAllByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup); - ListenableFuture> findAllByTo(EntityId to); + ListenableFuture> findAllByTo(EntityId to, RelationTypeGroup typeGroup); - ListenableFuture> findAllByToAndType(EntityId to, String relationType); + ListenableFuture> findAllByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup); - ListenableFuture checkRelation(EntityId from, EntityId to, String relationType); + ListenableFuture checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture saveRelation(EntityRelation relation); ListenableFuture deleteRelation(EntityRelation relation); - ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType); + ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture deleteOutboundRelations(EntityId entity); - ListenableFuture> findRelations(EntityId from, String relationType, EntityType toType, TimePageLink pageLink); + ListenableFuture> findRelations(EntityId from, String relationType, RelationTypeGroup typeGroup, EntityType toType, TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java index e89985eea8..868769ffe8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationInfo; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import java.util.List; @@ -27,25 +28,25 @@ import java.util.List; */ public interface RelationService { - ListenableFuture checkRelation(EntityId from, EntityId to, String relationType); + ListenableFuture checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture saveRelation(EntityRelation relation); ListenableFuture deleteRelation(EntityRelation relation); - ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType); + ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture deleteEntityRelations(EntityId entity); - ListenableFuture> findByFrom(EntityId from); + ListenableFuture> findByFrom(EntityId from, RelationTypeGroup typeGroup); - ListenableFuture> findInfoByFrom(EntityId from); + ListenableFuture> findInfoByFrom(EntityId from, RelationTypeGroup typeGroup); - ListenableFuture> findByFromAndType(EntityId from, String relationType); + ListenableFuture> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup); - ListenableFuture> findByTo(EntityId to); + ListenableFuture> findByTo(EntityId to, RelationTypeGroup typeGroup); - ListenableFuture> findByToAndType(EntityId to, String relationType); + ListenableFuture> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture> findByQuery(EntityRelationsQuery query); diff --git a/dao/src/main/resources/schema.cql b/dao/src/main/resources/schema.cql index b72d2f642f..71de6771af 100644 --- a/dao/src/main/resources/schema.cql +++ b/dao/src/main/resources/schema.cql @@ -307,24 +307,25 @@ CREATE TABLE IF NOT EXISTS thingsboard.relation ( from_type text, to_id timeuuid, to_type text, + relation_type_group text, relation_type text, additional_info text, - PRIMARY KEY ((from_id, from_type), relation_type, to_id, to_type) -) WITH CLUSTERING ORDER BY ( relation_type ASC, to_id ASC, to_type ASC); + PRIMARY KEY ((from_id, from_type), relation_type_group, relation_type, to_id, to_type) +) WITH CLUSTERING ORDER BY ( relation_type_group ASC, relation_type ASC, to_id ASC, to_type ASC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.relation_by_type_and_child_type AS SELECT * from thingsboard.relation - WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL - PRIMARY KEY ((from_id, from_type), relation_type, to_type, to_id) - WITH CLUSTERING ORDER BY ( relation_type ASC, to_type ASC, to_id DESC); + WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type_group IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL + PRIMARY KEY ((from_id, from_type), relation_type_group, relation_type, to_type, to_id) + WITH CLUSTERING ORDER BY ( relation_type_group ASC, relation_type ASC, to_type ASC, to_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.reverse_relation AS SELECT * from thingsboard.relation - WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL - PRIMARY KEY ((to_id, to_type), relation_type, from_id, from_type) - WITH CLUSTERING ORDER BY ( relation_type ASC, from_id ASC, from_type ASC); + WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type_group IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL + PRIMARY KEY ((to_id, to_type), relation_type_group, relation_type, from_id, from_type) + WITH CLUSTERING ORDER BY ( relation_type_group ASC, relation_type ASC, from_id ASC, from_type ASC); CREATE TABLE IF NOT EXISTS thingsboard.widgets_bundle ( id timeuuid, diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceImplTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceImplTest.java index 3f5c55affd..f7e47ce526 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceImplTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceImplTest.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.relation.EntityRelationsQuery; import org.thingsboard.server.dao.relation.EntitySearchDirection; @@ -55,13 +56,13 @@ public class RelationServiceImplTest extends AbstractServiceTest { Assert.assertTrue(saveRelation(relation)); - Assert.assertTrue(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertTrue(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); - Assert.assertFalse(relationService.checkRelation(parentId, childId, "NOT_EXISTING_TYPE").get()); + Assert.assertFalse(relationService.checkRelation(parentId, childId, "NOT_EXISTING_TYPE", RelationTypeGroup.COMMON).get()); - Assert.assertFalse(relationService.checkRelation(childId, parentId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertFalse(relationService.checkRelation(childId, parentId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); - Assert.assertFalse(relationService.checkRelation(childId, parentId, "NOT_EXISTING_TYPE").get()); + Assert.assertFalse(relationService.checkRelation(childId, parentId, "NOT_EXISTING_TYPE", RelationTypeGroup.COMMON).get()); } @Test @@ -78,11 +79,11 @@ public class RelationServiceImplTest extends AbstractServiceTest { Assert.assertTrue(relationService.deleteRelation(relationA).get()); - Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); - Assert.assertTrue(relationService.checkRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertTrue(relationService.checkRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); - Assert.assertTrue(relationService.deleteRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertTrue(relationService.deleteRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); } @Test @@ -99,9 +100,9 @@ public class RelationServiceImplTest extends AbstractServiceTest { Assert.assertTrue(relationService.deleteEntityRelations(childId).get()); - Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); - Assert.assertFalse(relationService.checkRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertFalse(relationService.checkRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); } @Test @@ -123,7 +124,7 @@ public class RelationServiceImplTest extends AbstractServiceTest { saveRelation(relationB1); saveRelation(relationB2); - List relations = relationService.findByFrom(parentA).get(); + List relations = relationService.findByFrom(parentA, RelationTypeGroup.COMMON).get(); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(EntityRelation.CONTAINS_TYPE, relation.getType()); @@ -131,13 +132,13 @@ public class RelationServiceImplTest extends AbstractServiceTest { Assert.assertTrue(childA.equals(relation.getTo()) || childB.equals(relation.getTo())); } - relations = relationService.findByFromAndType(parentA, EntityRelation.CONTAINS_TYPE).get(); + relations = relationService.findByFromAndType(parentA, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(2, relations.size()); - relations = relationService.findByFromAndType(parentA, EntityRelation.MANAGES_TYPE).get(); + relations = relationService.findByFromAndType(parentA, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(0, relations.size()); - relations = relationService.findByFrom(parentB).get(); + relations = relationService.findByFrom(parentB, RelationTypeGroup.COMMON).get(); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(EntityRelation.MANAGES_TYPE, relation.getType()); @@ -145,10 +146,10 @@ public class RelationServiceImplTest extends AbstractServiceTest { Assert.assertTrue(childA.equals(relation.getTo()) || childB.equals(relation.getTo())); } - relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE).get(); + relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(0, relations.size()); - relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE).get(); + relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(0, relations.size()); } @@ -178,26 +179,26 @@ public class RelationServiceImplTest extends AbstractServiceTest { // Data propagation to views is async Thread.sleep(3000); - List relations = relationService.findByTo(childA).get(); + List relations = relationService.findByTo(childA, RelationTypeGroup.COMMON).get(); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(childA, relation.getTo()); Assert.assertTrue(parentA.equals(relation.getFrom()) || parentB.equals(relation.getFrom())); } - relations = relationService.findByToAndType(childA, EntityRelation.CONTAINS_TYPE).get(); + relations = relationService.findByToAndType(childA, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(1, relations.size()); - relations = relationService.findByToAndType(childB, EntityRelation.MANAGES_TYPE).get(); + relations = relationService.findByToAndType(childB, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(1, relations.size()); - relations = relationService.findByToAndType(parentA, EntityRelation.MANAGES_TYPE).get(); + relations = relationService.findByToAndType(parentA, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(0, relations.size()); - relations = relationService.findByToAndType(parentB, EntityRelation.MANAGES_TYPE).get(); + relations = relationService.findByToAndType(parentB, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(0, relations.size()); - relations = relationService.findByTo(childB).get(); + relations = relationService.findByTo(childB, RelationTypeGroup.COMMON).get(); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(childB, relation.getTo()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceTest.java index 376a88dc49..8a082fc1da 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceTest.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.relation.EntityRelationsQuery; import org.thingsboard.server.dao.relation.EntitySearchDirection; @@ -55,13 +56,13 @@ public class RelationServiceTest extends AbstractServiceTest { Assert.assertTrue(saveRelation(relation)); - Assert.assertTrue(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertTrue(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); - Assert.assertFalse(relationService.checkRelation(parentId, childId, "NOT_EXISTING_TYPE").get()); + Assert.assertFalse(relationService.checkRelation(parentId, childId, "NOT_EXISTING_TYPE", RelationTypeGroup.COMMON).get()); - Assert.assertFalse(relationService.checkRelation(childId, parentId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertFalse(relationService.checkRelation(childId, parentId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); - Assert.assertFalse(relationService.checkRelation(childId, parentId, "NOT_EXISTING_TYPE").get()); + Assert.assertFalse(relationService.checkRelation(childId, parentId, "NOT_EXISTING_TYPE", RelationTypeGroup.COMMON).get()); } @Test @@ -78,11 +79,11 @@ public class RelationServiceTest extends AbstractServiceTest { Assert.assertTrue(relationService.deleteRelation(relationA).get()); - Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); - Assert.assertTrue(relationService.checkRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertTrue(relationService.checkRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); - Assert.assertTrue(relationService.deleteRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertTrue(relationService.deleteRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); } @Test @@ -99,9 +100,9 @@ public class RelationServiceTest extends AbstractServiceTest { Assert.assertTrue(relationService.deleteEntityRelations(childId).get()); - Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); - Assert.assertFalse(relationService.checkRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE).get()); + Assert.assertFalse(relationService.checkRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); } @Test @@ -123,7 +124,7 @@ public class RelationServiceTest extends AbstractServiceTest { saveRelation(relationB1); saveRelation(relationB2); - List relations = relationService.findByFrom(parentA).get(); + List relations = relationService.findByFrom(parentA, RelationTypeGroup.COMMON).get(); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(EntityRelation.CONTAINS_TYPE, relation.getType()); @@ -131,13 +132,13 @@ public class RelationServiceTest extends AbstractServiceTest { Assert.assertTrue(childA.equals(relation.getTo()) || childB.equals(relation.getTo())); } - relations = relationService.findByFromAndType(parentA, EntityRelation.CONTAINS_TYPE).get(); + relations = relationService.findByFromAndType(parentA, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(2, relations.size()); - relations = relationService.findByFromAndType(parentA, EntityRelation.MANAGES_TYPE).get(); + relations = relationService.findByFromAndType(parentA, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(0, relations.size()); - relations = relationService.findByFrom(parentB).get(); + relations = relationService.findByFrom(parentB, RelationTypeGroup.COMMON).get(); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(EntityRelation.MANAGES_TYPE, relation.getType()); @@ -145,10 +146,10 @@ public class RelationServiceTest extends AbstractServiceTest { Assert.assertTrue(childA.equals(relation.getTo()) || childB.equals(relation.getTo())); } - relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE).get(); + relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(0, relations.size()); - relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE).get(); + relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(0, relations.size()); } @@ -178,26 +179,26 @@ public class RelationServiceTest extends AbstractServiceTest { // Data propagation to views is async Thread.sleep(3000); - List relations = relationService.findByTo(childA).get(); + List relations = relationService.findByTo(childA, RelationTypeGroup.COMMON).get(); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(childA, relation.getTo()); Assert.assertTrue(parentA.equals(relation.getFrom()) || parentB.equals(relation.getFrom())); } - relations = relationService.findByToAndType(childA, EntityRelation.CONTAINS_TYPE).get(); + relations = relationService.findByToAndType(childA, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(1, relations.size()); - relations = relationService.findByToAndType(childB, EntityRelation.MANAGES_TYPE).get(); + relations = relationService.findByToAndType(childB, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(1, relations.size()); - relations = relationService.findByToAndType(parentA, EntityRelation.MANAGES_TYPE).get(); + relations = relationService.findByToAndType(parentA, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(0, relations.size()); - relations = relationService.findByToAndType(parentB, EntityRelation.MANAGES_TYPE).get(); + relations = relationService.findByToAndType(parentB, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); Assert.assertEquals(0, relations.size()); - relations = relationService.findByTo(childB).get(); + relations = relationService.findByTo(childB, RelationTypeGroup.COMMON).get(); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(childB, relation.getTo()); diff --git a/pom.xml b/pom.xml index fe95d9b9d9..9e59137d0b 100755 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,7 @@ 18.0 3.4 1.5.0 - 2.7.3 + 2.8.8.1 2.2.6 2.11 2.4.2 diff --git a/ui/src/app/entity/entity-select.directive.js b/ui/src/app/entity/entity-select.directive.js index 8988aaeccd..705c6b8627 100644 --- a/ui/src/app/entity/entity-select.directive.js +++ b/ui/src/app/entity/entity-select.directive.js @@ -29,7 +29,7 @@ export default function EntitySelect($compile, $templateCache) { element.html(template); scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; - scope.model = null; + scope.model = {}; scope.updateView = function () { if (!scope.disabled) { @@ -50,11 +50,11 @@ export default function EntitySelect($compile, $templateCache) { ngModelCtrl.$render = function () { if (ngModelCtrl.$viewValue) { var value = ngModelCtrl.$viewValue; - scope.model = {}; scope.model.entityType = value.entityType; scope.model.entityId = value.id; } else { - scope.model = null; + scope.model.entityType = null; + scope.model.entityId = null; } } diff --git a/ui/src/app/entity/entity-select.tpl.html b/ui/src/app/entity/entity-select.tpl.html index a354b1dcf6..ce0a16b483 100644 --- a/ui/src/app/entity/entity-select.tpl.html +++ b/ui/src/app/entity/entity-select.tpl.html @@ -19,7 +19,7 @@ - Date: Mon, 29 May 2017 23:47:45 -0400 Subject: [PATCH 37/37] Added method implementations to missing JPA Asset and Alalrm DAO methods --- .../common/data/asset/TenantAssetType.java | 7 ++ .../server/dao/sql/JpaAbstractDao.java | 1 - .../server/dao/sql/alarm/AlarmRepository.java | 2 +- .../server/dao/sql/alarm/JpaAlarmDao.java | 34 ++++++-- .../server/dao/sql/asset/AssetRepository.java | 16 ++++ .../server/dao/sql/asset/JpaAssetDao.java | 11 ++- .../dao/sql/relation/JpaRelationDao.java | 84 +++++++++++++++++++ .../server/dao/sql/alarm/JpaAlarmDaoTest.java | 5 ++ .../server/dao/sql/asset/JpaAssetDaoTest.java | 66 +++++++++++++-- 9 files changed, 205 insertions(+), 21 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java index bd23f2aa5a..8e0eb0e69e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java @@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.asset; import org.thingsboard.server.common.data.id.TenantId; +import java.util.UUID; + public class TenantAssetType { private static final long serialVersionUID = 8057290243855622101L; @@ -33,6 +35,11 @@ public class TenantAssetType { this.tenantId = tenantId; } + public TenantAssetType(String type, UUID tenantId) { + this.type = type; + this.tenantId = new TenantId(tenantId); + } + public String getType() { return type; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index 7073b491ac..a2b2bc9150 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -76,7 +76,6 @@ public abstract class JpaAbstractDao, D> implements Dao< @Override public ListenableFuture findByIdAsync(UUID key) { log.debug("Get entity by key async {}", key); - // Should ListeningExecutorService be a field? ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); ListenableFuture listenableFuture = service.submit(() -> DaoUtil.getData(getCrudRepository().findOne(key))); return listenableFuture; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index f14a08f7eb..e46c144692 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -27,7 +27,7 @@ import java.util.UUID; * Created by Valerii Sosliuk on 5/21/2017. */ @ConditionalOnProperty(prefix = "sql", value = "enabled", havingValue = "true", matchIfMissing = false) -public interface AlarmRepository extends CrudRepository { +public interface AlarmRepository extends CrudRepository { @Query(nativeQuery = true, value = "SELECT * FROM ALARM WHERE TENANT_ID = ?1 AND ORIGINATOR_ID = ?2 " + "AND ?3 = ?3 AND TYPE = ?4 ORDER BY ID DESC LIMIT 1") diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index 86ad73692e..cd0df63c32 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -15,23 +15,28 @@ */ package org.thingsboard.server.dao.sql.alarm; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.*; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.alarm.AlarmDao; +import org.thingsboard.server.dao.alarm.BaseAlarmService; import org.thingsboard.server.dao.model.sql.AlarmEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; +import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.Executors; @@ -41,6 +46,7 @@ import static org.springframework.transaction.annotation.Propagation.REQUIRES_NE /** * Created by Valerii Sosliuk on 5/19/2017. */ +@Slf4j @Component @ConditionalOnProperty(prefix = "sql", value = "enabled", havingValue = "true", matchIfMissing = false) public class JpaAlarmDao extends JpaAbstractDao implements AlarmDao { @@ -48,6 +54,9 @@ public class JpaAlarmDao extends JpaAbstractDao implements A @Autowired private AlarmRepository alarmRepository; + @Autowired + private RelationDao relationDao; + @Override protected Class getEntityClass() { return AlarmEntity.class; @@ -62,21 +71,28 @@ public class JpaAlarmDao extends JpaAbstractDao implements A @Transactional(propagation = REQUIRES_NEW) public ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); - ListenableFuture listenableFuture = service.submit(() -> DaoUtil.getData( + return service.submit(() -> DaoUtil.getData( alarmRepository.findLatestByOriginatorAndType(tenantId.getId(), originator.getId(), originator.getEntityType().ordinal(), type))); - return listenableFuture; } @Override public ListenableFuture findAlarmByIdAsync(UUID key) { - //TODO: Implement - return null; + return findByIdAsync(key); } @Override public ListenableFuture> findAlarms(AlarmQuery query) { - //TODO: Implement - return null; + log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink()); + EntityId affectedEntity = query.getAffectedEntityId(); + String relationType = query.getStatus() == null ? BaseAlarmService.ALARM_RELATION : BaseAlarmService.ALARM_RELATION_PREFIX + query.getStatus().name(); + ListenableFuture> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink()); + return Futures.transform(relations, (AsyncFunction, List>) input -> { + List> alarmFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + alarmFutures.add(findAlarmByIdAsync(relation.getTo().getId())); + } + return Futures.successfulAsList(alarmFutures); + }); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java index cf38c02988..b6c9891540 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java @@ -19,6 +19,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.TenantAssetType; import org.thingsboard.server.dao.model.sql.AssetEntity; import java.util.List; @@ -58,4 +59,19 @@ public interface AssetRepository extends CrudRepository { List findByTenantIdAndCustomerIdAndIdIn(UUID tenantId, UUID customerId, List assetIds); AssetEntity findByTenantIdAndName(UUID tenantId, String name); + + @Query(nativeQuery = true, value = "SELECT * FROM ASSET WHERE TENANT_ID = ?2 " + + "AND CUSTOMER_ID = ?3 AND TYPE = ?4 " + + "AND LOWER(SEARCH_TEXT) LIKE LOWER(CONCAT(?5, '%')) " + + "ORDER BY ID LIMIT ?1") + List findByTenantIdAndCustomerIdAndTypeFirstPage(int limit, UUID tenantId, UUID customerId, String type, String textSearch); + + @Query(nativeQuery = true, value = "SELECT * FROM ASSET WHERE TENANT_ID = ?2 " + + "AND CUSTOMER_ID = ?3 AND TYPE = ?4 " + + "AND LOWER(SEARCH_TEXT) LIKE LOWER(CONCAT(?5, '%')) " + + "AND ID > ?6 ORDER BY ID LIMIT ?1") + List findByTenantIdAndCustomerIdAndTypeNextPage(int limit, UUID tenantId, UUID customerId, String type, String textSearch, UUID idOffset); + + @Query(value = "SELECT NEW org.thingsboard.server.common.data.asset.TenantAssetType(a.type, a.tenantId) FROM AssetEntity a GROUP BY a.tenantId, a.type") + List findTenantAssetTypes(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index bc52aa088f..49f8d44e05 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -104,11 +104,18 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im @Override public List findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) { - return null; + if (pageLink.getIdOffset() == null) { + return DaoUtil.convertDataList(assetRepository.findByTenantIdAndCustomerIdAndTypeFirstPage(pageLink.getLimit(), tenantId, + customerId, type, pageLink.getTextSearch())); + } else { + return DaoUtil.convertDataList(assetRepository.findByTenantIdAndCustomerIdAndTypeNextPage(pageLink.getLimit(), tenantId, + customerId, type, pageLink.getTextSearch(), pageLink.getIdOffset())); + } } @Override public ListenableFuture> findTenantAssetTypesAsync() { - return null; + ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); + return service.submit(() -> assetRepository.findTenantAssetTypes()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java new file mode 100644 index 0000000000..beaa9c64ac --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java @@ -0,0 +1,84 @@ +package org.thingsboard.server.dao.sql.relation; + +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.dao.relation.RelationDao; + +import java.util.List; + +/** + * Created by Valerii Sosliuk on 5/29/2017. + */ +@Slf4j +@Component +@ConditionalOnProperty(prefix = "sql", value = "enabled", havingValue = "true", matchIfMissing = false) +public class JpaRelationDao implements RelationDao { + + + @Override + public ListenableFuture> findAllByFrom(EntityId from, RelationTypeGroup typeGroup) { + // TODO: Implement + return null; + } + + @Override + public ListenableFuture> findAllByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { + // TODO: Implement + return null; + } + + @Override + public ListenableFuture> findAllByTo(EntityId to, RelationTypeGroup typeGroup) { + // TODO: Implement + return null; + } + + @Override + public ListenableFuture> findAllByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { + // TODO: Implement + return null; + } + + @Override + public ListenableFuture checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { + // TODO: Implement + return null; + } + + @Override + public ListenableFuture saveRelation(EntityRelation relation) { + // TODO: Implement + return null; + } + + @Override + public ListenableFuture deleteRelation(EntityRelation relation) { + // TODO: Implement + return null; + } + + @Override + public ListenableFuture deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { + // TODO: Implement + return null; + } + + @Override + public ListenableFuture deleteOutboundRelations(EntityId entity) { + // TODO: Implement + return null; + } + + @Override + public ListenableFuture> findRelations(EntityId from, String relationType, RelationTypeGroup typeGroup, EntityType toType, TimePageLink pageLink) { + // TODO: Implement + return null; + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java index 94b6f51c33..b26db0bb97 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java @@ -71,6 +71,11 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { assertEquals(alarm2Id, alarm.getId().getId()); } + @Test + public void testFindAlarmByIdAsync() { + // TODO: implement + } + private void saveAlarm(UUID id, UUID tenantId, UUID deviceId, String type) { Alarm alarm = new Alarm(); alarm.setId(new AlarmId(id)); diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java index ebee17e4bc..4be102e089 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.TenantAssetType; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -32,6 +33,7 @@ import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; import static junit.framework.TestCase.assertFalse; import static org.junit.Assert.assertEquals; @@ -56,7 +58,7 @@ public class JpaAssetDaoTest extends AbstractJpaDaoTest { UUID assetId = UUIDs.timeBased(); UUID tenantId = i % 2 == 0 ? tenantId1 : tenantId2; UUID customerId = i % 2 == 0 ? customerId1 : customerId2; - saveAsset(assetId, tenantId, customerId, "ASSET_" + i); + saveAsset(assetId, tenantId, customerId, "ASSET_" + i, "TYPE_1"); } assertEquals(60, assetDao.find().size()); @@ -83,7 +85,7 @@ public class JpaAssetDaoTest extends AbstractJpaDaoTest { UUID assetId = UUIDs.timeBased(); UUID tenantId = i % 2 == 0 ? tenantId1 : tenantId2; UUID customerId = i % 2 == 0 ? customerId1 : customerId2; - saveAsset(assetId, tenantId, customerId, "ASSET_" + i); + saveAsset(assetId, tenantId, customerId, "ASSET_" + i, "TYPE_1"); } TextPageLink pageLink1 = new TextPageLink(20, "ASSET_"); @@ -106,7 +108,7 @@ public class JpaAssetDaoTest extends AbstractJpaDaoTest { List searchIds = new ArrayList<>(); for (int i = 0; i < 30; i++) { UUID assetId = UUIDs.timeBased(); - saveAsset(assetId, tenantId, customerId, "ASSET_" + i); + saveAsset(assetId, tenantId, customerId, "ASSET_" + i, "TYPE_1"); if (i % 3 == 0) { searchIds.add(assetId); } @@ -128,7 +130,7 @@ public class JpaAssetDaoTest extends AbstractJpaDaoTest { for (int i = 0; i < 30; i++) { UUID assetId = UUIDs.timeBased(); UUID customerId = i%2 == 0 ? customerId1 : customerId2; - saveAsset(assetId, tenantId, customerId, "ASSET_" + i); + saveAsset(assetId, tenantId, customerId, "ASSET_" + i, "TYPE_1"); if (i % 3 == 0) { searchIds.add(assetId); } @@ -150,8 +152,8 @@ public class JpaAssetDaoTest extends AbstractJpaDaoTest { UUID customerId1 = UUIDs.timeBased(); UUID customerId2 = UUIDs.timeBased(); String name = "TEST_ASSET"; - saveAsset(assetId1, tenantId1, customerId1, name); - saveAsset(assetId2, tenantId2, customerId2, name); + saveAsset(assetId1, tenantId1, customerId1, name, "TYPE_1"); + saveAsset(assetId2, tenantId2, customerId2, name, "TYPE_1"); Optional assetOpt1 = assetDao.findAssetsByTenantIdAndName(tenantId2, name); assertTrue("Optional expected to be non-empty", assetOpt1.isPresent()); @@ -161,13 +163,61 @@ public class JpaAssetDaoTest extends AbstractJpaDaoTest { assertFalse("Optional expected to be empty", assetOpt2.isPresent()); } - private void saveAsset(UUID id, UUID tenantId, UUID customerId, String name) { + @Test + public void testFindAssetsByTenantIdAndType() { + // TODO: implement + } + + @Test + public void testFindAssetsByTenantIdAndCustomerIdAndType() { + // TODO: implement + } + + @Test + public void testFindTenantAssetTypesAsync() throws ExecutionException, InterruptedException { + UUID assetId1 = UUIDs.timeBased(); + UUID assetId2 = UUIDs.timeBased(); + UUID tenantId1 = UUIDs.timeBased(); + UUID tenantId2 = UUIDs.timeBased(); + UUID customerId1 = UUIDs.timeBased(); + UUID customerId2 = UUIDs.timeBased(); + saveAsset(UUIDs.timeBased(), tenantId1, customerId1, "TEST_ASSET_1", "TYPE_1"); + saveAsset(UUIDs.timeBased(), tenantId1, customerId1, "TEST_ASSET_2", "TYPE_1"); + saveAsset(UUIDs.timeBased(), tenantId1, customerId1, "TEST_ASSET_3", "TYPE_2"); + saveAsset(UUIDs.timeBased(), tenantId1, customerId1, "TEST_ASSET_4", "TYPE_3"); + saveAsset(UUIDs.timeBased(), tenantId1, customerId1, "TEST_ASSET_5", "TYPE_3"); + saveAsset(UUIDs.timeBased(), tenantId1, customerId1, "TEST_ASSET_6", "TYPE_3"); + + saveAsset(UUIDs.timeBased(), tenantId2, customerId2, "TEST_ASSET_7", "TYPE_4"); + saveAsset(UUIDs.timeBased(), tenantId2, customerId2, "TEST_ASSET_8", "TYPE_1"); + saveAsset(UUIDs.timeBased(), tenantId2, customerId2, "TEST_ASSET_9", "TYPE_1"); + + ListenableFuture> tenantAssetTypesFuture = assetDao.findTenantAssetTypesAsync(); + List tenantAssetTypes = tenantAssetTypesFuture.get(); + assertNotNull(tenantAssetTypes); + List tenant1Types = tenantAssetTypes.stream().filter(t -> t.getTenantId().getId().equals(tenantId1)).collect(Collectors.toList()); + List tenant2Types = tenantAssetTypes.stream().filter(t -> t.getTenantId().getId().equals(tenantId2)).collect(Collectors.toList()); + + assertEquals(3, tenant1Types.size()); + assertTrue(tenant1Types.stream().anyMatch(t -> t.getType().equals("TYPE_1"))); + assertTrue(tenant1Types.stream().anyMatch(t -> t.getType().equals("TYPE_2"))); + assertTrue(tenant1Types.stream().anyMatch(t -> t.getType().equals("TYPE_3"))); + assertFalse(tenant1Types.stream().anyMatch(t -> t.getType().equals("TYPE_4"))); + + assertEquals(2, tenant2Types.size()); + assertTrue(tenant2Types.stream().anyMatch(t -> t.getType().equals("TYPE_1"))); + assertTrue(tenant2Types.stream().anyMatch(t -> t.getType().equals("TYPE_4"))); + assertFalse(tenant2Types.stream().anyMatch(t -> t.getType().equals("TYPE_2"))); + assertFalse(tenant2Types.stream().anyMatch(t -> t.getType().equals("TYPE_3"))); + } + + private void saveAsset(UUID id, UUID tenantId, UUID customerId, String name, String type) { Asset asset = new Asset(); asset.setId(new AssetId(id)); asset.setTenantId(new TenantId(tenantId)); asset.setCustomerId(new CustomerId(customerId)); asset.setName(name); - + asset.setType(type); assetDao.save(asset); } }