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); - } - ); + ); + } else { + 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; - } - return name; - } - 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