Commit a708509aacbab0a723833c29b28a0775a2f6ac77

Authored by Igor Kulikov
1 parent 90ef91e3

Entity relations management.

Showing 61 changed files with 1516 additions and 145 deletions
@@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*; @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*;
21 import org.thingsboard.server.common.data.id.EntityId; 21 import org.thingsboard.server.common.data.id.EntityId;
22 import org.thingsboard.server.common.data.id.EntityIdFactory; 22 import org.thingsboard.server.common.data.id.EntityIdFactory;
23 import org.thingsboard.server.common.data.relation.EntityRelation; 23 import org.thingsboard.server.common.data.relation.EntityRelation;
  24 +import org.thingsboard.server.common.data.relation.EntityRelationInfo;
24 import org.thingsboard.server.dao.relation.EntityRelationsQuery; 25 import org.thingsboard.server.dao.relation.EntityRelationsQuery;
25 import org.thingsboard.server.exception.ThingsboardErrorCode; 26 import org.thingsboard.server.exception.ThingsboardErrorCode;
26 import org.thingsboard.server.exception.ThingsboardException; 27 import org.thingsboard.server.exception.ThingsboardException;
@@ -128,6 +129,21 @@ public class EntityRelationController extends BaseController { @@ -128,6 +129,21 @@ public class EntityRelationController extends BaseController {
128 } 129 }
129 130
130 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 131 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  132 + @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"fromId", "fromType"})
  133 + @ResponseBody
  134 + public List<EntityRelationInfo> findInfoByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType) throws ThingsboardException {
  135 + checkParameter("fromId", strFromId);
  136 + checkParameter("fromType", strFromType);
  137 + EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
  138 + checkEntityId(entityId);
  139 + try {
  140 + return checkNotNull(relationService.findInfoByFrom(entityId).get());
  141 + } catch (Exception e) {
  142 + throw handleException(e);
  143 + }
  144 + }
  145 +
  146 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
131 @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"}) 147 @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"})
132 @ResponseBody 148 @ResponseBody
133 public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType 149 public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType
@@ -20,7 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -20,7 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId;
20 20
21 import com.fasterxml.jackson.databind.JsonNode; 21 import com.fasterxml.jackson.databind.JsonNode;
22 22
23 -public class Customer extends ContactBased<CustomerId>{ 23 +public class Customer extends ContactBased<CustomerId> implements HasName {
24 24
25 private static final long serialVersionUID = -1599722990298929275L; 25 private static final long serialVersionUID = -1599722990298929275L;
26 26
@@ -59,6 +59,11 @@ public class Customer extends ContactBased<CustomerId>{ @@ -59,6 +59,11 @@ public class Customer extends ContactBased<CustomerId>{
59 this.title = title; 59 this.title = title;
60 } 60 }
61 61
  62 + @Override
  63 + public String getName() {
  64 + return title;
  65 + }
  66 +
62 public JsonNode getAdditionalInfo() { 67 public JsonNode getAdditionalInfo() {
63 return additionalInfo; 68 return additionalInfo;
64 } 69 }
@@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.id.CustomerId; @@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
19 import org.thingsboard.server.common.data.id.DashboardId; 19 import org.thingsboard.server.common.data.id.DashboardId;
20 import org.thingsboard.server.common.data.id.TenantId; 20 import org.thingsboard.server.common.data.id.TenantId;
21 21
22 -public class DashboardInfo extends SearchTextBased<DashboardId> { 22 +public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName {
23 23
24 private TenantId tenantId; 24 private TenantId tenantId;
25 private CustomerId customerId; 25 private CustomerId customerId;
@@ -65,6 +65,11 @@ public class DashboardInfo extends SearchTextBased<DashboardId> { @@ -65,6 +65,11 @@ public class DashboardInfo extends SearchTextBased<DashboardId> {
65 } 65 }
66 66
67 @Override 67 @Override
  68 + public String getName() {
  69 + return title;
  70 + }
  71 +
  72 + @Override
68 public String getSearchText() { 73 public String getSearchText() {
69 return title; 74 return title;
70 } 75 }
@@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.id.TenantId;
21 21
22 import com.fasterxml.jackson.databind.JsonNode; 22 import com.fasterxml.jackson.databind.JsonNode;
23 23
24 -public class Device extends SearchTextBased<DeviceId> { 24 +public class Device extends SearchTextBased<DeviceId> implements HasName {
25 25
26 private static final long serialVersionUID = 2807343040519543363L; 26 private static final long serialVersionUID = 2807343040519543363L;
27 27
@@ -64,6 +64,7 @@ public class Device extends SearchTextBased<DeviceId> { @@ -64,6 +64,7 @@ public class Device extends SearchTextBased<DeviceId> {
64 this.customerId = customerId; 64 this.customerId = customerId;
65 } 65 }
66 66
  67 + @Override
67 public String getName() { 68 public String getName() {
68 return name; 69 return name;
69 } 70 }
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data;
  17 +
  18 +public interface HasName {
  19 +
  20 + String getName();
  21 +
  22 +}
@@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.id.TenantId;
19 19
20 import com.fasterxml.jackson.databind.JsonNode; 20 import com.fasterxml.jackson.databind.JsonNode;
21 21
22 -public class Tenant extends ContactBased<TenantId>{ 22 +public class Tenant extends ContactBased<TenantId> implements HasName {
23 23
24 private static final long serialVersionUID = 8057243243859922101L; 24 private static final long serialVersionUID = 8057243243859922101L;
25 25
@@ -50,6 +50,11 @@ public class Tenant extends ContactBased<TenantId>{ @@ -50,6 +50,11 @@ public class Tenant extends ContactBased<TenantId>{
50 this.title = title; 50 this.title = title;
51 } 51 }
52 52
  53 + @Override
  54 + public String getName() {
  55 + return title;
  56 + }
  57 +
53 public String getRegion() { 58 public String getRegion() {
54 return region; 59 return region;
55 } 60 }
@@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.security.Authority; @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.security.Authority;
22 22
23 import com.fasterxml.jackson.databind.JsonNode; 23 import com.fasterxml.jackson.databind.JsonNode;
24 24
25 -public class User extends SearchTextBased<UserId> { 25 +public class User extends SearchTextBased<UserId> implements HasName {
26 26
27 private static final long serialVersionUID = 8250339805336035966L; 27 private static final long serialVersionUID = 8250339805336035966L;
28 28
@@ -77,6 +77,11 @@ public class User extends SearchTextBased<UserId> { @@ -77,6 +77,11 @@ public class User extends SearchTextBased<UserId> {
77 this.email = email; 77 this.email = email;
78 } 78 }
79 79
  80 + @Override
  81 + public String getName() {
  82 + return email;
  83 + }
  84 +
80 public Authority getAuthority() { 85 public Authority getAuthority() {
81 return authority; 86 return authority;
82 } 87 }
@@ -18,13 +18,14 @@ package org.thingsboard.server.common.data.alarm; @@ -18,13 +18,14 @@ package org.thingsboard.server.common.data.alarm;
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 import lombok.Data; 19 import lombok.Data;
20 import org.thingsboard.server.common.data.BaseData; 20 import org.thingsboard.server.common.data.BaseData;
  21 +import org.thingsboard.server.common.data.HasName;
21 import org.thingsboard.server.common.data.id.EntityId; 22 import org.thingsboard.server.common.data.id.EntityId;
22 23
23 /** 24 /**
24 * Created by ashvayka on 11.05.17. 25 * Created by ashvayka on 11.05.17.
25 */ 26 */
26 @Data 27 @Data
27 -public class Alarm extends BaseData<AlarmId> { 28 +public class Alarm extends BaseData<AlarmId> implements HasName {
28 29
29 private long startTs; 30 private long startTs;
30 private long endTs; 31 private long endTs;
@@ -37,4 +38,8 @@ public class Alarm extends BaseData<AlarmId> { @@ -37,4 +38,8 @@ public class Alarm extends BaseData<AlarmId> {
37 private JsonNode details; 38 private JsonNode details;
38 private boolean propagate; 39 private boolean propagate;
39 40
  41 + @Override
  42 + public String getName() {
  43 + return type;
  44 + }
40 } 45 }
@@ -16,12 +16,13 @@ @@ -16,12 +16,13 @@
16 package org.thingsboard.server.common.data.asset; 16 package org.thingsboard.server.common.data.asset;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import org.thingsboard.server.common.data.HasName;
19 import org.thingsboard.server.common.data.SearchTextBased; 20 import org.thingsboard.server.common.data.SearchTextBased;
20 import org.thingsboard.server.common.data.id.AssetId; 21 import org.thingsboard.server.common.data.id.AssetId;
21 import org.thingsboard.server.common.data.id.CustomerId; 22 import org.thingsboard.server.common.data.id.CustomerId;
22 import org.thingsboard.server.common.data.id.TenantId; 23 import org.thingsboard.server.common.data.id.TenantId;
23 24
24 -public class Asset extends SearchTextBased<AssetId> { 25 +public class Asset extends SearchTextBased<AssetId> implements HasName {
25 26
26 private static final long serialVersionUID = 2807343040519543363L; 27 private static final long serialVersionUID = 2807343040519543363L;
27 28
@@ -64,6 +65,7 @@ public class Asset extends SearchTextBased<AssetId> { @@ -64,6 +65,7 @@ public class Asset extends SearchTextBased<AssetId> {
64 this.customerId = customerId; 65 this.customerId = customerId;
65 } 66 }
66 67
  68 + @Override
67 public String getName() { 69 public String getName() {
68 return name; 70 return name;
69 } 71 }
@@ -15,13 +15,14 @@ @@ -15,13 +15,14 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.plugin; 16 package org.thingsboard.server.common.data.plugin;
17 17
  18 +import org.thingsboard.server.common.data.HasName;
18 import org.thingsboard.server.common.data.SearchTextBased; 19 import org.thingsboard.server.common.data.SearchTextBased;
19 import org.thingsboard.server.common.data.id.PluginId; 20 import org.thingsboard.server.common.data.id.PluginId;
20 import org.thingsboard.server.common.data.id.TenantId; 21 import org.thingsboard.server.common.data.id.TenantId;
21 22
22 import com.fasterxml.jackson.databind.JsonNode; 23 import com.fasterxml.jackson.databind.JsonNode;
23 24
24 -public class PluginMetaData extends SearchTextBased<PluginId> { 25 +public class PluginMetaData extends SearchTextBased<PluginId> implements HasName {
25 26
26 private static final long serialVersionUID = 1L; 27 private static final long serialVersionUID = 1L;
27 28
@@ -75,6 +76,7 @@ public class PluginMetaData extends SearchTextBased<PluginId> { @@ -75,6 +76,7 @@ public class PluginMetaData extends SearchTextBased<PluginId> {
75 this.tenantId = tenantId; 76 this.tenantId = tenantId;
76 } 77 }
77 78
  79 + @Override
78 public String getName() { 80 public String getName() {
79 return name; 81 return name;
80 } 82 }
@@ -47,11 +47,11 @@ public class EntityRelation { @@ -47,11 +47,11 @@ public class EntityRelation {
47 this.additionalInfo = additionalInfo; 47 this.additionalInfo = additionalInfo;
48 } 48 }
49 49
50 - public EntityRelation(EntityRelation device) {  
51 - this.from = device.getFrom();  
52 - this.to = device.getTo();  
53 - this.type = device.getType();  
54 - this.additionalInfo = device.getAdditionalInfo(); 50 + public EntityRelation(EntityRelation entityRelation) {
  51 + this.from = entityRelation.getFrom();
  52 + this.to = entityRelation.getTo();
  53 + this.type = entityRelation.getType();
  54 + this.additionalInfo = entityRelation.getAdditionalInfo();
55 } 55 }
56 56
57 public EntityId getFrom() { 57 public EntityId getFrom() {
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package org.thingsboard.server.common.data.relation;
  18 +
  19 +public class EntityRelationInfo extends EntityRelation {
  20 +
  21 + private static final long serialVersionUID = 2807343097519543363L;
  22 +
  23 + private String toName;
  24 +
  25 + public EntityRelationInfo() {
  26 + super();
  27 + }
  28 +
  29 + public EntityRelationInfo(EntityRelation entityRelation) {
  30 + super(entityRelation);
  31 + }
  32 +
  33 + public String getToName() {
  34 + return toName;
  35 + }
  36 +
  37 + public void setToName(String toName) {
  38 + this.toName = toName;
  39 + }
  40 +
  41 + @Override
  42 + public boolean equals(Object o) {
  43 + if (this == o) return true;
  44 + if (o == null || getClass() != o.getClass()) return false;
  45 + if (!super.equals(o)) return false;
  46 +
  47 + EntityRelationInfo that = (EntityRelationInfo) o;
  48 +
  49 + return toName != null ? toName.equals(that.toName) : that.toName == null;
  50 +
  51 + }
  52 +
  53 + @Override
  54 + public int hashCode() {
  55 + int result = super.hashCode();
  56 + result = 31 * result + (toName != null ? toName.hashCode() : 0);
  57 + return result;
  58 + }
  59 +}
@@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.rule; @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.rule;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 import lombok.ToString; 19 import lombok.ToString;
  20 +import org.thingsboard.server.common.data.HasName;
20 import org.thingsboard.server.common.data.SearchTextBased; 21 import org.thingsboard.server.common.data.SearchTextBased;
21 import org.thingsboard.server.common.data.id.CustomerId; 22 import org.thingsboard.server.common.data.id.CustomerId;
22 import org.thingsboard.server.common.data.id.RuleId; 23 import org.thingsboard.server.common.data.id.RuleId;
@@ -26,7 +27,7 @@ import com.fasterxml.jackson.databind.JsonNode; @@ -26,7 +27,7 @@ import com.fasterxml.jackson.databind.JsonNode;
26 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; 27 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
27 28
28 @Data 29 @Data
29 -public class RuleMetaData extends SearchTextBased<RuleId> { 30 +public class RuleMetaData extends SearchTextBased<RuleId> implements HasName {
30 31
31 private static final long serialVersionUID = -5656679015122935465L; 32 private static final long serialVersionUID = -5656679015122935465L;
32 33
@@ -66,4 +67,9 @@ public class RuleMetaData extends SearchTextBased<RuleId> { @@ -66,4 +67,9 @@ public class RuleMetaData extends SearchTextBased<RuleId> {
66 return name; 67 return name;
67 } 68 }
68 69
  70 + @Override
  71 + public String getName() {
  72 + return name;
  73 + }
  74 +
69 } 75 }
@@ -28,6 +28,10 @@ import java.util.Optional; @@ -28,6 +28,10 @@ import java.util.Optional;
28 */ 28 */
29 public interface AlarmService { 29 public interface AlarmService {
30 30
  31 + Alarm findAlarmById(AlarmId alarmId);
  32 +
  33 + ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId);
  34 +
31 Optional<Alarm> saveIfNotExists(Alarm alarm); 35 Optional<Alarm> saveIfNotExists(Alarm alarm);
32 36
33 ListenableFuture<Boolean> updateAlarm(Alarm alarm); 37 ListenableFuture<Boolean> updateAlarm(Alarm alarm);
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.alarm;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.stereotype.Service;
  21 +import org.thingsboard.server.common.data.alarm.Alarm;
  22 +import org.thingsboard.server.common.data.alarm.AlarmId;
  23 +import org.thingsboard.server.common.data.alarm.AlarmQuery;
  24 +import org.thingsboard.server.common.data.page.TimePageData;
  25 +
  26 +import java.util.Optional;
  27 +
  28 +@Service
  29 +@Slf4j
  30 +public class BaseAlarmService implements AlarmService {
  31 +
  32 + @Override
  33 + public Alarm findAlarmById(AlarmId alarmId) {
  34 + return null;
  35 + }
  36 +
  37 + @Override
  38 + public ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId) {
  39 + return null;
  40 + }
  41 +
  42 + @Override
  43 + public Optional<Alarm> saveIfNotExists(Alarm alarm) {
  44 + return null;
  45 + }
  46 +
  47 + @Override
  48 + public ListenableFuture<Boolean> updateAlarm(Alarm alarm) {
  49 + return null;
  50 + }
  51 +
  52 + @Override
  53 + public ListenableFuture<Boolean> ackAlarm(Alarm alarm) {
  54 + return null;
  55 + }
  56 +
  57 + @Override
  58 + public ListenableFuture<Boolean> clearAlarm(AlarmId alarmId) {
  59 + return null;
  60 + }
  61 +
  62 + @Override
  63 + public ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query) {
  64 + return null;
  65 + }
  66 +}
@@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.page.TextPageData; @@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.page.TextPageData;
35 import org.thingsboard.server.common.data.page.TextPageLink; 35 import org.thingsboard.server.common.data.page.TextPageLink;
36 import org.thingsboard.server.common.data.relation.EntityRelation; 36 import org.thingsboard.server.common.data.relation.EntityRelation;
37 import org.thingsboard.server.dao.customer.CustomerDao; 37 import org.thingsboard.server.dao.customer.CustomerDao;
38 -import org.thingsboard.server.dao.entity.BaseEntityService; 38 +import org.thingsboard.server.dao.entity.AbstractEntityService;
39 import org.thingsboard.server.dao.exception.DataValidationException; 39 import org.thingsboard.server.dao.exception.DataValidationException;
40 import org.thingsboard.server.dao.model.*; 40 import org.thingsboard.server.dao.model.*;
41 import org.thingsboard.server.dao.relation.EntitySearchDirection; 41 import org.thingsboard.server.dao.relation.EntitySearchDirection;
@@ -55,7 +55,7 @@ import static org.thingsboard.server.dao.service.Validator.*; @@ -55,7 +55,7 @@ import static org.thingsboard.server.dao.service.Validator.*;
55 55
56 @Service 56 @Service
57 @Slf4j 57 @Slf4j
58 -public class BaseAssetService extends BaseEntityService implements AssetService { 58 +public class BaseAssetService extends AbstractEntityService implements AssetService {
59 59
60 @Autowired 60 @Autowired
61 private AssetDao assetDao; 61 private AssetDao assetDao;
@@ -31,17 +31,15 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -31,17 +31,15 @@ import com.google.common.util.concurrent.ListenableFuture;
31 import lombok.extern.slf4j.Slf4j; 31 import lombok.extern.slf4j.Slf4j;
32 import org.apache.commons.lang3.StringUtils; 32 import org.apache.commons.lang3.StringUtils;
33 import org.thingsboard.server.common.data.Customer; 33 import org.thingsboard.server.common.data.Customer;
34 -import org.thingsboard.server.common.data.asset.Asset;  
35 import org.thingsboard.server.common.data.id.CustomerId; 34 import org.thingsboard.server.common.data.id.CustomerId;
36 import org.thingsboard.server.common.data.id.TenantId; 35 import org.thingsboard.server.common.data.id.TenantId;
37 import org.thingsboard.server.common.data.page.TextPageData; 36 import org.thingsboard.server.common.data.page.TextPageData;
38 import org.thingsboard.server.common.data.page.TextPageLink; 37 import org.thingsboard.server.common.data.page.TextPageLink;
39 import org.thingsboard.server.dao.dashboard.DashboardService; 38 import org.thingsboard.server.dao.dashboard.DashboardService;
40 import org.thingsboard.server.dao.device.DeviceService; 39 import org.thingsboard.server.dao.device.DeviceService;
41 -import org.thingsboard.server.dao.entity.BaseEntityService; 40 +import org.thingsboard.server.dao.entity.AbstractEntityService;
42 import org.thingsboard.server.dao.exception.DataValidationException; 41 import org.thingsboard.server.dao.exception.DataValidationException;
43 import org.thingsboard.server.dao.exception.IncorrectParameterException; 42 import org.thingsboard.server.dao.exception.IncorrectParameterException;
44 -import org.thingsboard.server.dao.model.AssetEntity;  
45 import org.thingsboard.server.dao.model.CustomerEntity; 43 import org.thingsboard.server.dao.model.CustomerEntity;
46 import org.thingsboard.server.dao.model.TenantEntity; 44 import org.thingsboard.server.dao.model.TenantEntity;
47 import org.thingsboard.server.dao.service.DataValidator; 45 import org.thingsboard.server.dao.service.DataValidator;
@@ -53,7 +51,7 @@ import org.springframework.stereotype.Service; @@ -53,7 +51,7 @@ import org.springframework.stereotype.Service;
53 import org.thingsboard.server.dao.service.Validator; 51 import org.thingsboard.server.dao.service.Validator;
54 @Service 52 @Service
55 @Slf4j 53 @Slf4j
56 -public class CustomerServiceImpl extends BaseEntityService implements CustomerService { 54 +public class CustomerServiceImpl extends AbstractEntityService implements CustomerService {
57 55
58 private static final String PUBLIC_CUSTOMER_TITLE = "Public"; 56 private static final String PUBLIC_CUSTOMER_TITLE = "Public";
59 57
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.dao.dashboard; 16 package org.thingsboard.server.dao.dashboard;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 import org.thingsboard.server.common.data.Dashboard; 19 import org.thingsboard.server.common.data.Dashboard;
19 import org.thingsboard.server.common.data.DashboardInfo; 20 import org.thingsboard.server.common.data.DashboardInfo;
20 import org.thingsboard.server.common.data.id.CustomerId; 21 import org.thingsboard.server.common.data.id.CustomerId;
@@ -27,8 +28,12 @@ public interface DashboardService { @@ -27,8 +28,12 @@ public interface DashboardService {
27 28
28 public Dashboard findDashboardById(DashboardId dashboardId); 29 public Dashboard findDashboardById(DashboardId dashboardId);
29 30
  31 + public ListenableFuture<Dashboard> findDashboardByIdAsync(DashboardId dashboardId);
  32 +
30 public DashboardInfo findDashboardInfoById(DashboardId dashboardId); 33 public DashboardInfo findDashboardInfoById(DashboardId dashboardId);
31 34
  35 + public ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(DashboardId dashboardId);
  36 +
32 public Dashboard saveDashboard(Dashboard dashboard); 37 public Dashboard saveDashboard(Dashboard dashboard);
33 38
34 public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId); 39 public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId);
@@ -17,9 +17,13 @@ package org.thingsboard.server.dao.dashboard; @@ -17,9 +17,13 @@ package org.thingsboard.server.dao.dashboard;
17 17
18 import static org.thingsboard.server.dao.DaoUtil.convertDataList; 18 import static org.thingsboard.server.dao.DaoUtil.convertDataList;
19 import static org.thingsboard.server.dao.DaoUtil.getData; 19 import static org.thingsboard.server.dao.DaoUtil.getData;
  20 +import static org.thingsboard.server.dao.service.Validator.validateId;
20 21
21 import java.util.List; 22 import java.util.List;
22 23
  24 +import com.google.common.base.Function;
  25 +import com.google.common.util.concurrent.Futures;
  26 +import com.google.common.util.concurrent.ListenableFuture;
23 import lombok.extern.slf4j.Slf4j; 27 import lombok.extern.slf4j.Slf4j;
24 import org.apache.commons.lang3.StringUtils; 28 import org.apache.commons.lang3.StringUtils;
25 import org.thingsboard.server.common.data.Dashboard; 29 import org.thingsboard.server.common.data.Dashboard;
@@ -30,7 +34,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -30,7 +34,7 @@ import org.thingsboard.server.common.data.id.TenantId;
30 import org.thingsboard.server.common.data.page.TextPageData; 34 import org.thingsboard.server.common.data.page.TextPageData;
31 import org.thingsboard.server.common.data.page.TextPageLink; 35 import org.thingsboard.server.common.data.page.TextPageLink;
32 import org.thingsboard.server.dao.customer.CustomerDao; 36 import org.thingsboard.server.dao.customer.CustomerDao;
33 -import org.thingsboard.server.dao.entity.BaseEntityService; 37 +import org.thingsboard.server.dao.entity.AbstractEntityService;
34 import org.thingsboard.server.dao.exception.DataValidationException; 38 import org.thingsboard.server.dao.exception.DataValidationException;
35 import org.thingsboard.server.dao.model.*; 39 import org.thingsboard.server.dao.model.*;
36 import org.thingsboard.server.dao.service.DataValidator; 40 import org.thingsboard.server.dao.service.DataValidator;
@@ -42,7 +46,7 @@ import org.thingsboard.server.dao.service.Validator; @@ -42,7 +46,7 @@ import org.thingsboard.server.dao.service.Validator;
42 46
43 @Service 47 @Service
44 @Slf4j 48 @Slf4j
45 -public class DashboardServiceImpl extends BaseEntityService implements DashboardService { 49 +public class DashboardServiceImpl extends AbstractEntityService implements DashboardService {
46 50
47 @Autowired 51 @Autowired
48 private DashboardDao dashboardDao; 52 private DashboardDao dashboardDao;
@@ -65,6 +69,14 @@ public class DashboardServiceImpl extends BaseEntityService implements Dashboard @@ -65,6 +69,14 @@ public class DashboardServiceImpl extends BaseEntityService implements Dashboard
65 } 69 }
66 70
67 @Override 71 @Override
  72 + public ListenableFuture<Dashboard> findDashboardByIdAsync(DashboardId dashboardId) {
  73 + log.trace("Executing findDashboardByIdAsync [{}]", dashboardId);
  74 + validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
  75 + ListenableFuture<DashboardEntity> dashboardEntity = dashboardDao.findByIdAsync(dashboardId.getId());
  76 + return Futures.transform(dashboardEntity, (Function<? super DashboardEntity, ? extends Dashboard>) input -> getData(input));
  77 + }
  78 +
  79 + @Override
68 public DashboardInfo findDashboardInfoById(DashboardId dashboardId) { 80 public DashboardInfo findDashboardInfoById(DashboardId dashboardId) {
69 log.trace("Executing findDashboardInfoById [{}]", dashboardId); 81 log.trace("Executing findDashboardInfoById [{}]", dashboardId);
70 Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId); 82 Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
@@ -73,6 +85,14 @@ public class DashboardServiceImpl extends BaseEntityService implements Dashboard @@ -73,6 +85,14 @@ public class DashboardServiceImpl extends BaseEntityService implements Dashboard
73 } 85 }
74 86
75 @Override 87 @Override
  88 + public ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(DashboardId dashboardId) {
  89 + log.trace("Executing findDashboardInfoByIdAsync [{}]", dashboardId);
  90 + validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
  91 + ListenableFuture<DashboardInfoEntity> dashboardInfoEntity = dashboardInfoDao.findByIdAsync(dashboardId.getId());
  92 + return Futures.transform(dashboardInfoEntity, (Function<? super DashboardInfoEntity, ? extends DashboardInfo>) input -> getData(input));
  93 + }
  94 +
  95 + @Override
76 public Dashboard saveDashboard(Dashboard dashboard) { 96 public Dashboard saveDashboard(Dashboard dashboard) {
77 log.trace("Executing saveDashboard [{}]", dashboard); 97 log.trace("Executing saveDashboard [{}]", dashboard);
78 dashboardValidator.validate(dashboard); 98 dashboardValidator.validate(dashboard);
@@ -37,7 +37,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; @@ -37,7 +37,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
37 import org.thingsboard.server.common.data.security.DeviceCredentials; 37 import org.thingsboard.server.common.data.security.DeviceCredentials;
38 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 38 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
39 import org.thingsboard.server.dao.customer.CustomerDao; 39 import org.thingsboard.server.dao.customer.CustomerDao;
40 -import org.thingsboard.server.dao.entity.BaseEntityService; 40 +import org.thingsboard.server.dao.entity.AbstractEntityService;
41 import org.thingsboard.server.dao.exception.DataValidationException; 41 import org.thingsboard.server.dao.exception.DataValidationException;
42 import org.thingsboard.server.dao.model.CustomerEntity; 42 import org.thingsboard.server.dao.model.CustomerEntity;
43 import org.thingsboard.server.dao.model.DeviceEntity; 43 import org.thingsboard.server.dao.model.DeviceEntity;
@@ -58,7 +58,7 @@ import static org.thingsboard.server.dao.service.Validator.*; @@ -58,7 +58,7 @@ import static org.thingsboard.server.dao.service.Validator.*;
58 58
59 @Service 59 @Service
60 @Slf4j 60 @Slf4j
61 -public class DeviceServiceImpl extends BaseEntityService implements DeviceService { 61 +public class DeviceServiceImpl extends AbstractEntityService implements DeviceService {
62 62
63 @Autowired 63 @Autowired
64 private DeviceDao deviceDao; 64 private DeviceDao deviceDao;
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package org.thingsboard.server.dao.entity;
  18 +
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.beans.factory.annotation.Autowired;
  21 +import org.thingsboard.server.common.data.id.EntityId;
  22 +import org.thingsboard.server.dao.relation.RelationService;
  23 +
  24 +@Slf4j
  25 +public abstract class AbstractEntityService {
  26 +
  27 + @Autowired
  28 + protected RelationService relationService;
  29 +
  30 + protected void deleteEntityRelations(EntityId entityId) {
  31 + log.trace("Executing deleteEntityRelations [{}]", entityId);
  32 + relationService.deleteEntityRelations(entityId);
  33 + }
  34 +
  35 +
  36 +}
@@ -15,23 +15,102 @@ @@ -15,23 +15,102 @@
15 */ 15 */
16 package org.thingsboard.server.dao.entity; 16 package org.thingsboard.server.dao.entity;
17 17
  18 +import com.google.common.base.Function;
  19 +import com.google.common.util.concurrent.Futures;
  20 +import com.google.common.util.concurrent.ListenableFuture;
18 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
20 -import org.thingsboard.server.common.data.id.EntityId;  
21 -import org.thingsboard.server.dao.relation.RelationService; 23 +import org.springframework.stereotype.Service;
  24 +import org.thingsboard.server.common.data.*;
  25 +import org.thingsboard.server.common.data.alarm.AlarmId;
  26 +import org.thingsboard.server.common.data.id.*;
  27 +import org.thingsboard.server.dao.alarm.AlarmService;
  28 +import org.thingsboard.server.dao.asset.AssetService;
  29 +import org.thingsboard.server.dao.customer.CustomerService;
  30 +import org.thingsboard.server.dao.dashboard.DashboardService;
  31 +import org.thingsboard.server.dao.device.DeviceService;
  32 +import org.thingsboard.server.dao.plugin.PluginService;
  33 +import org.thingsboard.server.dao.rule.RuleService;
  34 +import org.thingsboard.server.dao.tenant.TenantService;
  35 +import org.thingsboard.server.dao.user.UserService;
22 36
23 /** 37 /**
24 * Created by ashvayka on 04.05.17. 38 * Created by ashvayka on 04.05.17.
25 */ 39 */
  40 +@Service
26 @Slf4j 41 @Slf4j
27 -public class BaseEntityService { 42 +public class BaseEntityService extends AbstractEntityService implements EntityService {
28 43
29 @Autowired 44 @Autowired
30 - protected RelationService relationService; 45 + private AssetService assetService;
31 46
32 - protected void deleteEntityRelations(EntityId entityId) {  
33 - log.trace("Executing deleteEntityRelations [{}]", entityId);  
34 - relationService.deleteEntityRelations(entityId); 47 + @Autowired
  48 + private DeviceService deviceService;
  49 +
  50 + @Autowired
  51 + private RuleService ruleService;
  52 +
  53 + @Autowired
  54 + private PluginService pluginService;
  55 +
  56 + @Autowired
  57 + private TenantService tenantService;
  58 +
  59 + @Autowired
  60 + private CustomerService customerService;
  61 +
  62 + @Autowired
  63 + private UserService userService;
  64 +
  65 + @Autowired
  66 + private DashboardService dashboardService;
  67 +
  68 + @Autowired
  69 + private AlarmService alarmService;
  70 +
  71 + @Override
  72 + public void deleteEntityRelations(EntityId entityId) {
  73 + super.deleteEntityRelations(entityId);
  74 + }
  75 +
  76 + @Override
  77 + public ListenableFuture<String> fetchEntityNameAsync(EntityId entityId) {
  78 + log.trace("Executing fetchEntityNameAsync [{}]", entityId);
  79 + ListenableFuture<String> entityName;
  80 + ListenableFuture<? extends HasName> hasName;
  81 + switch (entityId.getEntityType()) {
  82 + case ASSET:
  83 + hasName = assetService.findAssetByIdAsync(new AssetId(entityId.getId()));
  84 + break;
  85 + case DEVICE:
  86 + hasName = deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
  87 + break;
  88 + case RULE:
  89 + hasName = ruleService.findRuleByIdAsync(new RuleId(entityId.getId()));
  90 + break;
  91 + case PLUGIN:
  92 + hasName = pluginService.findPluginByIdAsync(new PluginId(entityId.getId()));
  93 + break;
  94 + case TENANT:
  95 + hasName = tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
  96 + break;
  97 + case CUSTOMER:
  98 + hasName = customerService.findCustomerByIdAsync(new CustomerId(entityId.getId()));
  99 + break;
  100 + case USER:
  101 + hasName = userService.findUserByIdAsync(new UserId(entityId.getId()));
  102 + break;
  103 + case DASHBOARD:
  104 + hasName = dashboardService.findDashboardInfoByIdAsync(new DashboardId(entityId.getId()));
  105 + break;
  106 + case ALARM:
  107 + hasName = alarmService.findAlarmByIdAsync(new AlarmId(entityId.getId()));
  108 + break;
  109 + default:
  110 + throw new IllegalStateException("Not Implemented!");
  111 + }
  112 + entityName = Futures.transform(hasName, (Function<HasName, String>) hasName1 -> hasName1.getName() );
  113 + return entityName;
35 } 114 }
36 115
37 } 116 }
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package org.thingsboard.server.dao.entity;
  18 +
  19 +import com.google.common.util.concurrent.ListenableFuture;
  20 +import org.thingsboard.server.common.data.id.EntityId;
  21 +
  22 +public interface EntityService {
  23 +
  24 + ListenableFuture<String> fetchEntityNameAsync(EntityId entityId);
  25 +
  26 + void deleteEntityRelations(EntityId entityId);
  27 +
  28 +}
@@ -22,7 +22,6 @@ import org.apache.commons.lang3.StringUtils; @@ -22,7 +22,6 @@ import org.apache.commons.lang3.StringUtils;
22 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
24 import org.thingsboard.server.common.data.id.PluginId; 24 import org.thingsboard.server.common.data.id.PluginId;
25 -import org.thingsboard.server.common.data.id.RuleId;  
26 import org.thingsboard.server.common.data.id.TenantId; 25 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.data.page.TextPageData; 26 import org.thingsboard.server.common.data.page.TextPageData;
28 import org.thingsboard.server.common.data.page.TextPageLink; 27 import org.thingsboard.server.common.data.page.TextPageLink;
@@ -30,9 +29,8 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor; @@ -30,9 +29,8 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
30 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; 29 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
31 import org.thingsboard.server.common.data.plugin.ComponentType; 30 import org.thingsboard.server.common.data.plugin.ComponentType;
32 import org.thingsboard.server.common.data.plugin.PluginMetaData; 31 import org.thingsboard.server.common.data.plugin.PluginMetaData;
33 -import org.thingsboard.server.common.data.rule.RuleMetaData;  
34 import org.thingsboard.server.dao.component.ComponentDescriptorService; 32 import org.thingsboard.server.dao.component.ComponentDescriptorService;
35 -import org.thingsboard.server.dao.entity.BaseEntityService; 33 +import org.thingsboard.server.dao.entity.AbstractEntityService;
36 import org.thingsboard.server.dao.exception.DataValidationException; 34 import org.thingsboard.server.dao.exception.DataValidationException;
37 import org.thingsboard.server.dao.exception.DatabaseException; 35 import org.thingsboard.server.dao.exception.DatabaseException;
38 import org.thingsboard.server.dao.exception.IncorrectParameterException; 36 import org.thingsboard.server.dao.exception.IncorrectParameterException;
@@ -55,7 +53,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @@ -55,7 +53,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
55 53
56 @Service 54 @Service
57 @Slf4j 55 @Slf4j
58 -public class BasePluginService extends BaseEntityService implements PluginService { 56 +public class BasePluginService extends AbstractEntityService implements PluginService {
59 57
60 //TODO: move to a better place. 58 //TODO: move to a better place.
61 public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); 59 public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
@@ -23,9 +23,24 @@ import lombok.extern.slf4j.Slf4j; @@ -23,9 +23,24 @@ import lombok.extern.slf4j.Slf4j;
23 import org.springframework.beans.factory.annotation.Autowired; 23 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
25 import org.springframework.util.StringUtils; 25 import org.springframework.util.StringUtils;
  26 +import org.thingsboard.server.common.data.BaseData;
  27 +import org.thingsboard.server.common.data.Device;
  28 +import org.thingsboard.server.common.data.EntityType;
  29 +import org.thingsboard.server.common.data.asset.Asset;
  30 +import org.thingsboard.server.common.data.id.AssetId;
  31 +import org.thingsboard.server.common.data.id.DeviceId;
26 import org.thingsboard.server.common.data.id.EntityId; 32 import org.thingsboard.server.common.data.id.EntityId;
  33 +import org.thingsboard.server.common.data.id.UUIDBased;
27 import org.thingsboard.server.common.data.relation.EntityRelation; 34 import org.thingsboard.server.common.data.relation.EntityRelation;
  35 +import org.thingsboard.server.common.data.relation.EntityRelationInfo;
  36 +import org.thingsboard.server.dao.asset.AssetService;
  37 +import org.thingsboard.server.dao.customer.CustomerService;
  38 +import org.thingsboard.server.dao.device.DeviceService;
  39 +import org.thingsboard.server.dao.entity.EntityService;
28 import org.thingsboard.server.dao.exception.DataValidationException; 40 import org.thingsboard.server.dao.exception.DataValidationException;
  41 +import org.thingsboard.server.dao.plugin.PluginService;
  42 +import org.thingsboard.server.dao.rule.RuleService;
  43 +import org.thingsboard.server.dao.tenant.TenantService;
29 44
30 import javax.annotation.Nullable; 45 import javax.annotation.Nullable;
31 import java.util.*; 46 import java.util.*;
@@ -41,6 +56,9 @@ public class BaseRelationService implements RelationService { @@ -41,6 +56,9 @@ public class BaseRelationService implements RelationService {
41 @Autowired 56 @Autowired
42 private RelationDao relationDao; 57 private RelationDao relationDao;
43 58
  59 + @Autowired
  60 + private EntityService entityService;
  61 +
44 @Override 62 @Override
45 public ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType) { 63 public ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType) {
46 log.trace("Executing checkRelation [{}][{}][{}]", from, to, relationType); 64 log.trace("Executing checkRelation [{}][{}][{}]", from, to, relationType);
@@ -100,6 +118,31 @@ public class BaseRelationService implements RelationService { @@ -100,6 +118,31 @@ public class BaseRelationService implements RelationService {
100 } 118 }
101 119
102 @Override 120 @Override
  121 + public ListenableFuture<List<EntityRelationInfo>> findInfoByFrom(EntityId from) {
  122 + log.trace("Executing findInfoByFrom [{}]", from);
  123 + validate(from);
  124 + ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFrom(from);
  125 + ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations,
  126 + (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> {
  127 + List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
  128 + relations1.stream().forEach(relation -> futures.add(fetchRelationInfoAsync(relation)));
  129 + return Futures.successfulAsList(futures);
  130 + });
  131 + return relationsInfo;
  132 + }
  133 +
  134 + private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation) {
  135 + ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(relation.getTo());
  136 + ListenableFuture<EntityRelationInfo> entityRelationInfo =
  137 + Futures.transform(entityName, (Function<String, EntityRelationInfo>) entityName1 -> {
  138 + EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation);
  139 + entityRelationInfo1.setToName(entityName1);
  140 + return entityRelationInfo1;
  141 + });
  142 + return entityRelationInfo;
  143 + }
  144 +
  145 + @Override
103 public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType) { 146 public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType) {
104 log.trace("Executing findByFromAndType [{}][{}]", from, relationType); 147 log.trace("Executing findByFromAndType [{}][{}]", from, relationType);
105 validate(from); 148 validate(from);
@@ -18,6 +18,7 @@ package org.thingsboard.server.dao.relation; @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.relation;
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.server.common.data.id.EntityId; 19 import org.thingsboard.server.common.data.id.EntityId;
20 import org.thingsboard.server.common.data.relation.EntityRelation; 20 import org.thingsboard.server.common.data.relation.EntityRelation;
  21 +import org.thingsboard.server.common.data.relation.EntityRelationInfo;
21 22
22 import java.util.List; 23 import java.util.List;
23 24
@@ -38,6 +39,8 @@ public interface RelationService { @@ -38,6 +39,8 @@ public interface RelationService {
38 39
39 ListenableFuture<List<EntityRelation>> findByFrom(EntityId from); 40 ListenableFuture<List<EntityRelation>> findByFrom(EntityId from);
40 41
  42 + ListenableFuture<List<EntityRelationInfo>> findInfoByFrom(EntityId from);
  43 +
41 ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType); 44 ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType);
42 45
43 ListenableFuture<List<EntityRelation>> findByTo(EntityId to); 46 ListenableFuture<List<EntityRelation>> findByTo(EntityId to);
@@ -23,7 +23,6 @@ import lombok.extern.slf4j.Slf4j; @@ -23,7 +23,6 @@ import lombok.extern.slf4j.Slf4j;
23 import org.apache.commons.lang3.StringUtils; 23 import org.apache.commons.lang3.StringUtils;
24 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
26 -import org.thingsboard.server.common.data.asset.Asset;  
27 import org.thingsboard.server.common.data.id.RuleId; 26 import org.thingsboard.server.common.data.id.RuleId;
28 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.common.data.page.TextPageData; 28 import org.thingsboard.server.common.data.page.TextPageData;
@@ -34,11 +33,10 @@ import org.thingsboard.server.common.data.plugin.ComponentType; @@ -34,11 +33,10 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
34 import org.thingsboard.server.common.data.plugin.PluginMetaData; 33 import org.thingsboard.server.common.data.plugin.PluginMetaData;
35 import org.thingsboard.server.common.data.rule.RuleMetaData; 34 import org.thingsboard.server.common.data.rule.RuleMetaData;
36 import org.thingsboard.server.dao.component.ComponentDescriptorService; 35 import org.thingsboard.server.dao.component.ComponentDescriptorService;
37 -import org.thingsboard.server.dao.entity.BaseEntityService; 36 +import org.thingsboard.server.dao.entity.AbstractEntityService;
38 import org.thingsboard.server.dao.exception.DataValidationException; 37 import org.thingsboard.server.dao.exception.DataValidationException;
39 import org.thingsboard.server.dao.exception.DatabaseException; 38 import org.thingsboard.server.dao.exception.DatabaseException;
40 import org.thingsboard.server.dao.exception.IncorrectParameterException; 39 import org.thingsboard.server.dao.exception.IncorrectParameterException;
41 -import org.thingsboard.server.dao.model.AssetEntity;  
42 import org.thingsboard.server.dao.model.RuleMetaDataEntity; 40 import org.thingsboard.server.dao.model.RuleMetaDataEntity;
43 import org.thingsboard.server.dao.plugin.PluginService; 41 import org.thingsboard.server.dao.plugin.PluginService;
44 import org.thingsboard.server.dao.service.DataValidator; 42 import org.thingsboard.server.dao.service.DataValidator;
@@ -58,7 +56,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink; @@ -58,7 +56,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink;
58 56
59 @Service 57 @Service
60 @Slf4j 58 @Slf4j
61 -public class BaseRuleService extends BaseEntityService implements RuleService { 59 +public class BaseRuleService extends AbstractEntityService implements RuleService {
62 60
63 private final TenantId systemTenantId = new TenantId(NULL_UUID); 61 private final TenantId systemTenantId = new TenantId(NULL_UUID);
64 62
@@ -26,7 +26,6 @@ import com.google.common.util.concurrent.Futures; @@ -26,7 +26,6 @@ import com.google.common.util.concurrent.Futures;
26 import com.google.common.util.concurrent.ListenableFuture; 26 import com.google.common.util.concurrent.ListenableFuture;
27 import lombok.extern.slf4j.Slf4j; 27 import lombok.extern.slf4j.Slf4j;
28 import org.apache.commons.lang3.StringUtils; 28 import org.apache.commons.lang3.StringUtils;
29 -import org.thingsboard.server.common.data.Customer;  
30 import org.thingsboard.server.common.data.Tenant; 29 import org.thingsboard.server.common.data.Tenant;
31 import org.thingsboard.server.common.data.id.TenantId; 30 import org.thingsboard.server.common.data.id.TenantId;
32 import org.thingsboard.server.common.data.page.TextPageData; 31 import org.thingsboard.server.common.data.page.TextPageData;
@@ -34,9 +33,8 @@ import org.thingsboard.server.common.data.page.TextPageLink; @@ -34,9 +33,8 @@ import org.thingsboard.server.common.data.page.TextPageLink;
34 import org.thingsboard.server.dao.customer.CustomerService; 33 import org.thingsboard.server.dao.customer.CustomerService;
35 import org.thingsboard.server.dao.dashboard.DashboardService; 34 import org.thingsboard.server.dao.dashboard.DashboardService;
36 import org.thingsboard.server.dao.device.DeviceService; 35 import org.thingsboard.server.dao.device.DeviceService;
37 -import org.thingsboard.server.dao.entity.BaseEntityService; 36 +import org.thingsboard.server.dao.entity.AbstractEntityService;
38 import org.thingsboard.server.dao.exception.DataValidationException; 37 import org.thingsboard.server.dao.exception.DataValidationException;
39 -import org.thingsboard.server.dao.model.CustomerEntity;  
40 import org.thingsboard.server.dao.model.TenantEntity; 38 import org.thingsboard.server.dao.model.TenantEntity;
41 import org.thingsboard.server.dao.plugin.PluginService; 39 import org.thingsboard.server.dao.plugin.PluginService;
42 import org.thingsboard.server.dao.rule.RuleService; 40 import org.thingsboard.server.dao.rule.RuleService;
@@ -50,7 +48,7 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService; @@ -50,7 +48,7 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService;
50 48
51 @Service 49 @Service
52 @Slf4j 50 @Slf4j
53 -public class TenantServiceImpl extends BaseEntityService implements TenantService { 51 +public class TenantServiceImpl extends AbstractEntityService implements TenantService {
54 52
55 private static final String DEFAULT_TENANT_REGION = "Global"; 53 private static final String DEFAULT_TENANT_REGION = "Global";
56 54
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.dao.user; 16 package org.thingsboard.server.dao.user;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 import org.thingsboard.server.common.data.User; 19 import org.thingsboard.server.common.data.User;
19 import org.thingsboard.server.common.data.id.CustomerId; 20 import org.thingsboard.server.common.data.id.CustomerId;
20 import org.thingsboard.server.common.data.id.TenantId; 21 import org.thingsboard.server.common.data.id.TenantId;
@@ -27,6 +28,8 @@ public interface UserService { @@ -27,6 +28,8 @@ public interface UserService {
27 28
28 public User findUserById(UserId userId); 29 public User findUserById(UserId userId);
29 30
  31 + public ListenableFuture<User> findUserByIdAsync(UserId userId);
  32 +
30 public User findUserByEmail(String email); 33 public User findUserByEmail(String email);
31 34
32 public User saveUser(User user); 35 public User saveUser(User user);
@@ -23,6 +23,9 @@ import static org.thingsboard.server.dao.service.Validator.validateString; @@ -23,6 +23,9 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
23 23
24 import java.util.List; 24 import java.util.List;
25 25
  26 +import com.google.common.base.Function;
  27 +import com.google.common.util.concurrent.Futures;
  28 +import com.google.common.util.concurrent.ListenableFuture;
26 import lombok.extern.slf4j.Slf4j; 29 import lombok.extern.slf4j.Slf4j;
27 import org.apache.commons.lang3.RandomStringUtils; 30 import org.apache.commons.lang3.RandomStringUtils;
28 import org.apache.commons.lang3.StringUtils; 31 import org.apache.commons.lang3.StringUtils;
@@ -35,7 +38,7 @@ import org.thingsboard.server.common.data.page.TextPageLink; @@ -35,7 +38,7 @@ import org.thingsboard.server.common.data.page.TextPageLink;
35 import org.thingsboard.server.common.data.security.Authority; 38 import org.thingsboard.server.common.data.security.Authority;
36 import org.thingsboard.server.common.data.security.UserCredentials; 39 import org.thingsboard.server.common.data.security.UserCredentials;
37 import org.thingsboard.server.dao.customer.CustomerDao; 40 import org.thingsboard.server.dao.customer.CustomerDao;
38 -import org.thingsboard.server.dao.entity.BaseEntityService; 41 +import org.thingsboard.server.dao.entity.AbstractEntityService;
39 import org.thingsboard.server.dao.exception.DataValidationException; 42 import org.thingsboard.server.dao.exception.DataValidationException;
40 import org.thingsboard.server.dao.exception.IncorrectParameterException; 43 import org.thingsboard.server.dao.exception.IncorrectParameterException;
41 import org.thingsboard.server.dao.model.*; 44 import org.thingsboard.server.dao.model.*;
@@ -47,7 +50,7 @@ import org.springframework.stereotype.Service; @@ -47,7 +50,7 @@ import org.springframework.stereotype.Service;
47 50
48 @Service 51 @Service
49 @Slf4j 52 @Slf4j
50 -public class UserServiceImpl extends BaseEntityService implements UserService { 53 +public class UserServiceImpl extends AbstractEntityService implements UserService {
51 54
52 @Autowired 55 @Autowired
53 private UserDao userDao; 56 private UserDao userDao;
@@ -78,6 +81,14 @@ public class UserServiceImpl extends BaseEntityService implements UserService { @@ -78,6 +81,14 @@ public class UserServiceImpl extends BaseEntityService implements UserService {
78 } 81 }
79 82
80 @Override 83 @Override
  84 + public ListenableFuture<User> findUserByIdAsync(UserId userId) {
  85 + log.trace("Executing findUserByIdAsync [{}]", userId);
  86 + validateId(userId, "Incorrect userId " + userId);
  87 + ListenableFuture<UserEntity> userEntity = userDao.findByIdAsync(userId.getId());
  88 + return Futures.transform(userEntity, (Function<? super UserEntity, ? extends User>) input -> getData(input));
  89 + }
  90 +
  91 + @Override
81 public User saveUser(User user) { 92 public User saveUser(User user) {
82 log.trace("Executing saveUser [{}]", user); 93 log.trace("Executing saveUser [{}]", user);
83 userValidator.validate(user); 94 userValidator.validate(user);
@@ -25,6 +25,7 @@ function EntityRelationService($http, $q) { @@ -25,6 +25,7 @@ function EntityRelationService($http, $q) {
25 deleteRelation: deleteRelation, 25 deleteRelation: deleteRelation,
26 deleteRelations: deleteRelations, 26 deleteRelations: deleteRelations,
27 findByFrom: findByFrom, 27 findByFrom: findByFrom,
  28 + findInfoByFrom: findInfoByFrom,
28 findByFromAndType: findByFromAndType, 29 findByFromAndType: findByFromAndType,
29 findByTo: findByTo, 30 findByTo: findByTo,
30 findByToAndType: findByToAndType, 31 findByToAndType: findByToAndType,
@@ -84,6 +85,18 @@ function EntityRelationService($http, $q) { @@ -84,6 +85,18 @@ function EntityRelationService($http, $q) {
84 return deferred.promise; 85 return deferred.promise;
85 } 86 }
86 87
  88 + function findInfoByFrom(fromId, fromType) {
  89 + var deferred = $q.defer();
  90 + var url = '/api/relations/info?fromId=' + fromId;
  91 + url += '&fromType=' + fromType;
  92 + $http.get(url, null).then(function success(response) {
  93 + deferred.resolve(response.data);
  94 + }, function fail() {
  95 + deferred.reject();
  96 + });
  97 + return deferred.promise;
  98 + }
  99 +
87 function findByFromAndType(fromId, fromType, relationType) { 100 function findByFromAndType(fromId, fromType, relationType) {
88 var deferred = $q.defer(); 101 var deferred = $q.defer();
89 var url = '/api/relations?fromId=' + fromId; 102 var url = '/api/relations?fromId=' + fromId;
@@ -20,14 +20,13 @@ export default angular.module('thingsboard.api.entity', [thingsboardTypes]) @@ -20,14 +20,13 @@ export default angular.module('thingsboard.api.entity', [thingsboardTypes])
20 .name; 20 .name;
21 21
22 /*@ngInject*/ 22 /*@ngInject*/
23 -function EntityService($http, $q, $filter, $translate, userService, deviceService, 23 +function EntityService($http, $q, $filter, $translate, $log, userService, deviceService,
24 assetService, tenantService, customerService, 24 assetService, tenantService, customerService,
25 - ruleService, pluginService, entityRelationService, attributeService, types, utils) { 25 + ruleService, pluginService, dashboardService, entityRelationService, attributeService, types, utils) {
26 var service = { 26 var service = {
27 getEntity: getEntity, 27 getEntity: getEntity,
28 getEntities: getEntities, 28 getEntities: getEntities,
29 getEntitiesByNameFilter: getEntitiesByNameFilter, 29 getEntitiesByNameFilter: getEntitiesByNameFilter,
30 - entityName: entityName,  
31 processEntityAliases: processEntityAliases, 30 processEntityAliases: processEntityAliases,
32 getEntityKeys: getEntityKeys, 31 getEntityKeys: getEntityKeys,
33 checkEntityAlias: checkEntityAlias, 32 checkEntityAlias: checkEntityAlias,
@@ -63,6 +62,15 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic @@ -63,6 +62,15 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
63 case types.entityType.plugin: 62 case types.entityType.plugin:
64 promise = pluginService.getPlugin(entityId); 63 promise = pluginService.getPlugin(entityId);
65 break; 64 break;
  65 + case types.entityType.dashboard:
  66 + promise = dashboardService.getDashboardInfo(entityId);
  67 + break;
  68 + case types.entityType.user:
  69 + promise = userService.getUser(entityId);
  70 + break;
  71 + case types.entityType.alarm:
  72 + $log.error('Get Alarm Entity is not implemented!');
  73 + break;
66 } 74 }
67 return promise; 75 return promise;
68 } 76 }
@@ -134,6 +142,15 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic @@ -134,6 +142,15 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
134 case types.entityType.plugin: 142 case types.entityType.plugin:
135 promise = getEntitiesByIdsPromise(pluginService.getPlugin, entityIds); 143 promise = getEntitiesByIdsPromise(pluginService.getPlugin, entityIds);
136 break; 144 break;
  145 + case types.entityType.dashboard:
  146 + promise = getEntitiesByIdsPromise(dashboardService.getDashboardInfo, entityIds);
  147 + break;
  148 + case types.entityType.user:
  149 + promise = getEntitiesByIdsPromise(userService.getUser, entityIds);
  150 + break;
  151 + case types.entityType.alarm:
  152 + $log.error('Get Alarm Entity is not implemented!');
  153 + break;
137 } 154 }
138 return promise; 155 return promise;
139 } 156 }
@@ -141,34 +158,38 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic @@ -141,34 +158,38 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
141 function getEntities(entityType, entityIds, config) { 158 function getEntities(entityType, entityIds, config) {
142 var deferred = $q.defer(); 159 var deferred = $q.defer();
143 var promise = getEntitiesPromise(entityType, entityIds, config); 160 var promise = getEntitiesPromise(entityType, entityIds, config);
144 - promise.then(  
145 - function success(result) {  
146 - deferred.resolve(result);  
147 - },  
148 - function fail() {  
149 - deferred.reject();  
150 - }  
151 - ); 161 + if (promise) {
  162 + promise.then(
  163 + function success(result) {
  164 + deferred.resolve(result);
  165 + },
  166 + function fail() {
  167 + deferred.reject();
  168 + }
  169 + );
  170 + } else {
  171 + deferred.reject();
  172 + }
152 return deferred.promise; 173 return deferred.promise;
153 } 174 }
154 175
155 - function getEntitiesByPageLinkPromise(entityType, pageLink, config) { 176 + function getEntitiesByPageLinkPromise(entityType, pageLink, config, subType) {
156 var promise; 177 var promise;
157 var user = userService.getCurrentUser(); 178 var user = userService.getCurrentUser();
158 var customerId = user.customerId; 179 var customerId = user.customerId;
159 switch (entityType) { 180 switch (entityType) {
160 case types.entityType.device: 181 case types.entityType.device:
161 if (user.authority === 'CUSTOMER_USER') { 182 if (user.authority === 'CUSTOMER_USER') {
162 - promise = deviceService.getCustomerDevices(customerId, pageLink, false, config); 183 + promise = deviceService.getCustomerDevices(customerId, pageLink, false, config, subType);
163 } else { 184 } else {
164 - promise = deviceService.getTenantDevices(pageLink, false, config); 185 + promise = deviceService.getTenantDevices(pageLink, false, config, subType);
165 } 186 }
166 break; 187 break;
167 case types.entityType.asset: 188 case types.entityType.asset:
168 if (user.authority === 'CUSTOMER_USER') { 189 if (user.authority === 'CUSTOMER_USER') {
169 - promise = assetService.getCustomerAssets(customerId, pageLink, false, config); 190 + promise = assetService.getCustomerAssets(customerId, pageLink, false, config, subType);
170 } else { 191 } else {
171 - promise = assetService.getTenantAssets(pageLink, false, config); 192 + promise = assetService.getTenantAssets(pageLink, false, config, subType);
172 } 193 }
173 break; 194 break;
174 case types.entityType.tenant: 195 case types.entityType.tenant:
@@ -183,48 +204,48 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic @@ -183,48 +204,48 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
183 case types.entityType.plugin: 204 case types.entityType.plugin:
184 promise = pluginService.getAllPlugins(pageLink); 205 promise = pluginService.getAllPlugins(pageLink);
185 break; 206 break;
  207 + case types.entityType.dashboard:
  208 + if (user.authority === 'CUSTOMER_USER') {
  209 + promise = dashboardService.getCustomerDashboards(customerId, pageLink);
  210 + } else {
  211 + promise = dashboardService.getTenantDashboards(pageLink);
  212 + }
  213 + break;
  214 + case types.entityType.user:
  215 + $log.error('Get User Entities is not implemented!');
  216 + break;
  217 + case types.entityType.alarm:
  218 + $log.error('Get Alarm Entities is not implemented!');
  219 + break;
186 } 220 }
187 return promise; 221 return promise;
188 } 222 }
189 223
190 - function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config) { 224 + function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) {
191 var deferred = $q.defer(); 225 var deferred = $q.defer();
192 var pageLink = {limit: limit, textSearch: entityNameFilter}; 226 var pageLink = {limit: limit, textSearch: entityNameFilter};
193 - var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config);  
194 - promise.then(  
195 - function success(result) {  
196 - if (result.data && result.data.length > 0) {  
197 - deferred.resolve(result.data);  
198 - } else { 227 + var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType);
  228 + if (promise) {
  229 + promise.then(
  230 + function success(result) {
  231 + if (result.data && result.data.length > 0) {
  232 + deferred.resolve(result.data);
  233 + } else {
  234 + deferred.resolve(null);
  235 + }
  236 + },
  237 + function fail() {
199 deferred.resolve(null); 238 deferred.resolve(null);
200 } 239 }
201 - },  
202 - function fail() {  
203 - deferred.resolve(null);  
204 - }  
205 - );  
206 - return deferred.promise;  
207 - }  
208 -  
209 - function entityName(entityType, entity) {  
210 - var name = '';  
211 - switch (entityType) {  
212 - case types.entityType.device:  
213 - case types.entityType.asset:  
214 - case types.entityType.rule:  
215 - case types.entityType.plugin:  
216 - name = entity.name;  
217 - break;  
218 - case types.entityType.tenant:  
219 - case types.entityType.customer:  
220 - name = entity.title;  
221 - break; 240 + );
  241 + } else {
  242 + deferred.resolve(null);
222 } 243 }
223 - return name; 244 + return deferred.promise;
224 } 245 }
225 246
226 function entityToEntityInfo(entityType, entity) { 247 function entityToEntityInfo(entityType, entity) {
227 - return { name: entityName(entityType, entity), entityType: entityType, id: entity.id.id }; 248 + return { name: entity.name, entityType: entityType, id: entity.id.id };
228 } 249 }
229 250
230 function entitiesToEntitiesInfo(entityType, entities) { 251 function entitiesToEntitiesInfo(entityType, entities) {
@@ -55,4 +55,10 @@ @@ -55,4 +55,10 @@
55 default-event-type="{{vm.types.eventType.alarm.value}}"> 55 default-event-type="{{vm.types.eventType.alarm.value}}">
56 </tb-event-table> 56 </tb-event-table>
57 </md-tab> 57 </md-tab>
  58 + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
  59 + <tb-relation-table flex
  60 + entity-id="vm.grid.operatingItem().id.id"
  61 + entity-type="{{vm.types.entityType.asset}}">
  62 + </tb-relation-table>
  63 + </md-tab>
58 </tb-grid> 64 </tb-grid>
@@ -98,7 +98,10 @@ export default angular.module('thingsboard.types', []) @@ -98,7 +98,10 @@ export default angular.module('thingsboard.types', [])
98 rule: "RULE", 98 rule: "RULE",
99 plugin: "PLUGIN", 99 plugin: "PLUGIN",
100 tenant: "TENANT", 100 tenant: "TENANT",
101 - customer: "CUSTOMER" 101 + customer: "CUSTOMER",
  102 + user: "USER",
  103 + dashboard: "DASHBOARD",
  104 + alarm: "ALARM"
102 }, 105 },
103 entitySearchDirection: { 106 entitySearchDirection: {
104 from: "FROM", 107 from: "FROM",
@@ -108,7 +108,8 @@ function Utils($mdColorPalette, $rootScope, $window, types) { @@ -108,7 +108,8 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
108 guid: guid, 108 guid: guid,
109 isLocalUrl: isLocalUrl, 109 isLocalUrl: isLocalUrl,
110 validateDatasources: validateDatasources, 110 validateDatasources: validateDatasources,
111 - createKey: createKey 111 + createKey: createKey,
  112 + entityTypeName: entityTypeName
112 } 113 }
113 114
114 return service; 115 return service;
@@ -346,4 +347,27 @@ function Utils($mdColorPalette, $rootScope, $window, types) { @@ -346,4 +347,27 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
346 return dataKey; 347 return dataKey;
347 } 348 }
348 349
  350 + function entityTypeName (type) {
  351 + switch (type) {
  352 + case types.entityType.device:
  353 + return 'entity.type-device';
  354 + case types.entityType.asset:
  355 + return 'entity.type-asset';
  356 + case types.entityType.rule:
  357 + return 'entity.type-rule';
  358 + case types.entityType.plugin:
  359 + return 'entity.type-plugin';
  360 + case types.entityType.tenant:
  361 + return 'entity.type-tenant';
  362 + case types.entityType.customer:
  363 + return 'entity.type-customer';
  364 + case types.entityType.user:
  365 + return 'entity.type-user';
  366 + case types.entityType.dashboard:
  367 + return 'entity.type-dashboard';
  368 + case types.entityType.alarm:
  369 + return 'entity.type-alarm';
  370 + }
  371 + }
  372 +
349 } 373 }
@@ -34,7 +34,7 @@ @@ -34,7 +34,7 @@
34 </md-item-template> 34 </md-item-template>
35 <md-not-found> 35 <md-not-found>
36 <div class="tb-not-found"> 36 <div class="tb-not-found">
37 - <span translate translate-values='{ dashboard: dashboardSearchText }'>dashboard.no-dashboards-matching</span> 37 + <span translate translate-values='{ entity: dashboardSearchText }'>dashboard.no-dashboards-matching</span>
38 </div> 38 </div>
39 </md-not-found> 39 </md-not-found>
40 <div ng-messages="theForm.dashboard.$error"> 40 <div ng-messages="theForm.dashboard.$error">
@@ -34,7 +34,7 @@ @@ -34,7 +34,7 @@
34 </md-item-template> 34 </md-item-template>
35 <md-not-found> 35 <md-not-found>
36 <div class="tb-not-found"> 36 <div class="tb-not-found">
37 - <span translate translate-values='{ plugin: pluginSearchText }'>plugin.no-plugins-matching</span> 37 + <span translate translate-values='{ entity: pluginSearchText }'>plugin.no-plugins-matching</span>
38 </div> 38 </div>
39 </md-not-found> 39 </md-not-found>
40 </md-autocomplete> 40 </md-autocomplete>
@@ -109,7 +109,7 @@ export default function EntityStateController($scope, $location, $state, $stateP @@ -109,7 +109,7 @@ export default function EntityStateController($scope, $location, $state, $stateP
109 if (params && params.entityId && params.entityId.id && params.entityId.entityType) { 109 if (params && params.entityId && params.entityId.id && params.entityId.entityType) {
110 entityService.getEntity(params.entityId.entityType, params.entityId.id, {ignoreLoading: true, ignoreErrors: true}).then( 110 entityService.getEntity(params.entityId.entityType, params.entityId.id, {ignoreLoading: true, ignoreErrors: true}).then(
111 function success(entity) { 111 function success(entity) {
112 - var entityName = entityService.entityName(params.entityId.entityType, entity); 112 + var entityName = entity.name;
113 deferred.resolve(entityName); 113 deferred.resolve(entityName);
114 }, 114 },
115 function fail() { 115 function fail() {
@@ -128,9 +128,9 @@ @@ -128,9 +128,9 @@
128 <table md-table md-row-select multiple="" ng-model="selectedAttributes" md-progress="attributesDeferred.promise"> 128 <table md-table md-row-select multiple="" ng-model="selectedAttributes" md-progress="attributesDeferred.promise">
129 <thead md-head md-order="query.order" md-on-reorder="onReorder"> 129 <thead md-head md-order="query.order" md-on-reorder="onReorder">
130 <tr md-row> 130 <tr md-row>
131 - <th md-column md-order-by="lastUpdateTs"><span>Last update time</span></th>  
132 - <th md-column md-order-by="key"><span>Key</span></th>  
133 - <th md-column>Value</th> 131 + <th md-column md-order-by="lastUpdateTs"><span translate>attribute.last-update-time</span></th>
  132 + <th md-column md-order-by="key"><span translate>attribute.key</span></th>
  133 + <th md-column><span translate>attribute.value</span></th>
134 </tr> 134 </tr>
135 </thead> 135 </thead>
136 <tbody md-body> 136 <tbody md-body>
@@ -110,7 +110,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc @@ -110,7 +110,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
110 entityAlias.changed = false; 110 entityAlias.changed = false;
111 } 111 }
112 if (!entityAlias.changed && entity && entityAlias.entityType) { 112 if (!entityAlias.changed && entity && entityAlias.entityType) {
113 - entityAlias.alias = entityService.entityName(entityAlias.entityType, entity); 113 + entityAlias.alias = entity.name;
114 } 114 }
115 } 115 }
116 } 116 }
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import './entity-autocomplete.scss';
  17 +
  18 +/* eslint-disable import/no-unresolved, import/default */
  19 +
  20 +import entityAutocompleteTemplate from './entity-autocomplete.tpl.html';
  21 +
  22 +/* eslint-enable import/no-unresolved, import/default */
  23 +
  24 +/*@ngInject*/
  25 +export default function EntityAutocomplete($compile, $templateCache, $q, $filter, entityService, types) {
  26 +
  27 + var linker = function (scope, element, attrs, ngModelCtrl) {
  28 + var template = $templateCache.get(entityAutocompleteTemplate);
  29 + element.html(template);
  30 +
  31 + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
  32 + scope.entity = null;
  33 + scope.entitySearchText = '';
  34 +
  35 + scope.fetchEntities = function(searchText) {
  36 + var deferred = $q.defer();
  37 + entityService.getEntitiesByNameFilter(scope.entityType, searchText, 50, null, scope.entitySubtype).then(function success(result) {
  38 + if (result) {
  39 + deferred.resolve(result);
  40 + } else {
  41 + deferred.resolve([]);
  42 + }
  43 + }, function fail() {
  44 + deferred.reject();
  45 + });
  46 + return deferred.promise;
  47 + }
  48 +
  49 + scope.entitySearchTextChanged = function() {
  50 + }
  51 +
  52 + scope.updateView = function () {
  53 + if (!scope.disabled) {
  54 + ngModelCtrl.$setViewValue(scope.entity ? scope.entity.id.id : null);
  55 + }
  56 + }
  57 +
  58 + ngModelCtrl.$render = function () {
  59 + if (ngModelCtrl.$viewValue) {
  60 + entityService.getEntity(scope.entityType, ngModelCtrl.$viewValue).then(
  61 + function success(entity) {
  62 + scope.entity = entity;
  63 + },
  64 + function fail() {
  65 + scope.entity = null;
  66 + }
  67 + );
  68 + } else {
  69 + scope.entity = null;
  70 + }
  71 + }
  72 +
  73 + scope.$watch('entityType', function () {
  74 + load();
  75 + });
  76 +
  77 + scope.$watch('entitySubtype', function () {
  78 + if (scope.entity && scope.entity.type != scope.entitySubtype) {
  79 + scope.entity = null;
  80 + scope.updateView();
  81 + }
  82 + });
  83 +
  84 + scope.$watch('entity', function () {
  85 + scope.updateView();
  86 + });
  87 +
  88 + scope.$watch('disabled', function () {
  89 + scope.updateView();
  90 + });
  91 +
  92 +
  93 + function load() {
  94 + switch (scope.entityType) {
  95 + case types.entityType.asset:
  96 + scope.selectEntityText = 'asset.select-asset';
  97 + scope.entityText = 'asset.asset';
  98 + scope.noEntitiesMatchingText = 'asset.no-assets-matching';
  99 + scope.entityRequiredText = 'asset.asset-required'
  100 + break;
  101 + case types.entityType.device:
  102 + scope.selectEntityText = 'device.select-device';
  103 + scope.entityText = 'device.device';
  104 + scope.noEntitiesMatchingText = 'device.no-devices-matching';
  105 + scope.entityRequiredText = 'device.device-required'
  106 + break;
  107 + case types.entityType.rule:
  108 + scope.selectEntityText = 'rule.select-rule';
  109 + scope.entityText = 'rule.rule';
  110 + scope.noEntitiesMatchingText = 'rule.no-rules-matching';
  111 + scope.entityRequiredText = 'rule.rule-required'
  112 + break;
  113 + case types.entityType.plugin:
  114 + scope.selectEntityText = 'plugin.select-plugin';
  115 + scope.entityText = 'plugin.plugin';
  116 + scope.noEntitiesMatchingText = 'plugin.no-plugins-matching';
  117 + scope.entityRequiredText = 'plugin.plugin-required'
  118 + break;
  119 + case types.entityType.tenant:
  120 + scope.selectEntityText = 'tenant.select-tenant';
  121 + scope.entityText = 'tenant.tenant';
  122 + scope.noEntitiesMatchingText = 'tenant.no-tenants-matching';
  123 + scope.entityRequiredText = 'tenant.tenant-required'
  124 + break;
  125 + case types.entityType.customer:
  126 + scope.selectEntityText = 'customer.select-customer';
  127 + scope.entityText = 'customer.customer';
  128 + scope.noEntitiesMatchingText = 'customer.no-customers-matching';
  129 + scope.entityRequiredText = 'customer.customer-required'
  130 + break;
  131 + case types.entityType.user:
  132 + scope.selectEntityText = 'user.select-user';
  133 + scope.entityText = 'user.user';
  134 + scope.noEntitiesMatchingText = 'user.no-users-matching';
  135 + scope.entityRequiredText = 'user.user-required'
  136 + break;
  137 + case types.entityType.dashboard:
  138 + scope.selectEntityText = 'dashboard.select-dashboard';
  139 + scope.entityText = 'dashboard.dashboard';
  140 + scope.noEntitiesMatchingText = 'dashboard.no-dashboards-matching';
  141 + scope.entityRequiredText = 'dashboard.dashboard-required'
  142 + break;
  143 + case types.entityType.alarm:
  144 + scope.selectEntityText = 'alarm.select-alarm';
  145 + scope.entityText = 'alarm.alarm';
  146 + scope.noEntitiesMatchingText = 'alarm.no-alarms-matching';
  147 + scope.entityRequiredText = 'alarm.alarm-required'
  148 + break;
  149 + }
  150 + if (scope.entity && scope.entity.id.entityType != scope.entityType) {
  151 + scope.entity = null;
  152 + scope.updateView();
  153 + }
  154 + }
  155 +
  156 + $compile(element.contents())(scope);
  157 + }
  158 +
  159 + return {
  160 + restrict: "E",
  161 + require: "^ngModel",
  162 + link: linker,
  163 + scope: {
  164 + theForm: '=?',
  165 + tbRequired: '=?',
  166 + disabled:'=ngDisabled',
  167 + entityType: '=',
  168 + entitySubtype: '=?'
  169 + }
  170 + };
  171 +}
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +.tb-entity-autocomplete {
  17 + .tb-entity-item {
  18 + display: block;
  19 + height: 48px;
  20 + }
  21 + li {
  22 + height: auto !important;
  23 + white-space: normal !important;
  24 + }
  25 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<md-autocomplete ng-required="tbRequired"
  19 + ng-disabled="disabled"
  20 + md-no-cache="true"
  21 + md-input-name="entity"
  22 + ng-model="entity"
  23 + md-selected-item="entity"
  24 + md-search-text="entitySearchText"
  25 + md-search-text-change="entitySearchTextChanged()"
  26 + md-items="item in fetchEntities(entitySearchText)"
  27 + md-item-text="item.name"
  28 + md-min-length="0"
  29 + md-floating-label="{{ entityText | translate }}"
  30 + md-select-on-match="true"
  31 + md-menu-class="tb-entity-autocomplete">
  32 + <md-item-template>
  33 + <div class="tb-entity-item">
  34 + <span md-highlight-text="entitySearchText" md-highlight-flags="^i">{{item.name}}</span>
  35 + </div>
  36 + </md-item-template>
  37 + <md-not-found>
  38 + <div class="tb-not-found">
  39 + <span translate translate-values='{ entity: entitySearchText }'>{{ noEntitiesMatchingText }}</span>
  40 + </div>
  41 + </md-not-found>
  42 + <div ng-messages="theForm.entity.$error">
  43 + <div translate ng-message="required">{{ entityRequiredText }}</div>
  44 + </div>
  45 +</md-autocomplete>
@@ -32,14 +32,6 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti @@ -32,14 +32,6 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
32 32
33 scope.ngModelCtrl = ngModelCtrl; 33 scope.ngModelCtrl = ngModelCtrl;
34 34
35 - scope.itemName = function(item) {  
36 - if (item) {  
37 - return entityService.entityName(scope.entityType, item);  
38 - } else {  
39 - return '';  
40 - }  
41 - }  
42 -  
43 scope.fetchEntities = function(searchText, limit) { 35 scope.fetchEntities = function(searchText, limit) {
44 var deferred = $q.defer(); 36 var deferred = $q.defer();
45 entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(function success(result) { 37 entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(function success(result) {
@@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
33 md-min-length="0" 33 md-min-length="0"
34 placeholder="{{ 'entity.entity-list' | translate }}"> 34 placeholder="{{ 'entity.entity-list' | translate }}">
35 <md-item-template> 35 <md-item-template>
36 - <span md-highlight-text="entitySearchText" md-highlight-flags="^i">{{itemName(item)}}</span> 36 + <span md-highlight-text="entitySearchText" md-highlight-flags="^i">{{item.name}}</span>
37 </md-item-template> 37 </md-item-template>
38 <md-not-found> 38 <md-not-found>
39 <span translate translate-values='{ entity: entitySearchText }'>entity.no-entities-matching</span> 39 <span translate translate-values='{ entity: entitySearchText }'>entity.no-entities-matching</span>
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import './entity-select.scss';
  17 +
  18 +/* eslint-disable import/no-unresolved, import/default */
  19 +
  20 +import entitySelectTemplate from './entity-select.tpl.html';
  21 +
  22 +/* eslint-enable import/no-unresolved, import/default */
  23 +
  24 +/*@ngInject*/
  25 +export default function EntitySelect($compile, $templateCache) {
  26 +
  27 + var linker = function (scope, element, attrs, ngModelCtrl) {
  28 + var template = $templateCache.get(entitySelectTemplate);
  29 + element.html(template);
  30 +
  31 + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
  32 + scope.model = null;
  33 +
  34 + scope.updateView = function () {
  35 + if (!scope.disabled) {
  36 + var value = ngModelCtrl.$viewValue;
  37 + if (scope.model && scope.model.entityType && scope.model.entityId) {
  38 + if (!value) {
  39 + value = {};
  40 + }
  41 + value.entityType = scope.model.entityType;
  42 + value.id = scope.model.entityId;
  43 + ngModelCtrl.$setViewValue(value);
  44 + } else {
  45 + ngModelCtrl.$setViewValue(null);
  46 + }
  47 + }
  48 + }
  49 +
  50 + ngModelCtrl.$render = function () {
  51 + if (ngModelCtrl.$viewValue) {
  52 + var value = ngModelCtrl.$viewValue;
  53 + scope.model = {};
  54 + scope.model.entityType = value.entityType;
  55 + scope.model.entityId = value.id;
  56 + } else {
  57 + scope.model = null;
  58 + }
  59 + }
  60 +
  61 + scope.$watch('model.entityType', function () {
  62 + scope.updateView();
  63 + });
  64 +
  65 + scope.$watch('model.entityId', function () {
  66 + scope.updateView();
  67 + });
  68 +
  69 + scope.$watch('disabled', function () {
  70 + scope.updateView();
  71 + });
  72 +
  73 + $compile(element.contents())(scope);
  74 + }
  75 +
  76 + return {
  77 + restrict: "E",
  78 + require: "^ngModel",
  79 + link: linker,
  80 + scope: {
  81 + theForm: '=?',
  82 + tbRequired: '=?',
  83 + disabled:'=ngDisabled'
  84 + }
  85 + };
  86 +}
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +.tb-entity-select {
  18 +
  19 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<div layout='row' class="tb-entity-select">
  19 + <tb-entity-type-select style="min-width: 100px;"
  20 + ng-model="model.entityType">
  21 + </tb-entity-type-select>
  22 + <tb-entity-autocomplete flex
  23 + the-form="theForm"
  24 + ng-disabled="disabled"
  25 + tb-required="tbRequired"
  26 + entity-type="model.entityType"
  27 + ng-model="model.entityId">
  28 + </tb-entity-autocomplete>
  29 +</div>
@@ -23,7 +23,7 @@ import entityTypeSelectTemplate from './entity-type-select.tpl.html'; @@ -23,7 +23,7 @@ import entityTypeSelectTemplate from './entity-type-select.tpl.html';
23 /* eslint-enable import/no-unresolved, import/default */ 23 /* eslint-enable import/no-unresolved, import/default */
24 24
25 /*@ngInject*/ 25 /*@ngInject*/
26 -export default function EntityTypeSelect($compile, $templateCache, userService, types) { 26 +export default function EntityTypeSelect($compile, $templateCache, utils, userService, types) {
27 27
28 var linker = function (scope, element, attrs, ngModelCtrl) { 28 var linker = function (scope, element, attrs, ngModelCtrl) {
29 var template = $templateCache.get(entityTypeSelectTemplate); 29 var template = $templateCache.get(entityTypeSelectTemplate);
@@ -51,10 +51,12 @@ export default function EntityTypeSelect($compile, $templateCache, userService, @@ -51,10 +51,12 @@ export default function EntityTypeSelect($compile, $templateCache, userService,
51 scope.entityTypes.customer = types.entityType.customer; 51 scope.entityTypes.customer = types.entityType.customer;
52 scope.entityTypes.rule = types.entityType.rule; 52 scope.entityTypes.rule = types.entityType.rule;
53 scope.entityTypes.plugin = types.entityType.plugin; 53 scope.entityTypes.plugin = types.entityType.plugin;
  54 + scope.entityTypes.dashboard = types.entityType.dashboard;
54 break; 55 break;
55 case 'CUSTOMER_USER': 56 case 'CUSTOMER_USER':
56 scope.entityTypes.device = types.entityType.device; 57 scope.entityTypes.device = types.entityType.device;
57 scope.entityTypes.asset = types.entityType.asset; 58 scope.entityTypes.asset = types.entityType.asset;
  59 + scope.entityTypes.dashboard = types.entityType.dashboard;
58 break; 60 break;
59 } 61 }
60 62
@@ -67,20 +69,7 @@ export default function EntityTypeSelect($compile, $templateCache, userService, @@ -67,20 +69,7 @@ export default function EntityTypeSelect($compile, $templateCache, userService,
67 } 69 }
68 70
69 scope.typeName = function(type) { 71 scope.typeName = function(type) {
70 - switch (type) {  
71 - case types.entityType.device:  
72 - return 'entity.type-device';  
73 - case types.entityType.asset:  
74 - return 'entity.type-asset';  
75 - case types.entityType.rule:  
76 - return 'entity.type-rule';  
77 - case types.entityType.plugin:  
78 - return 'entity.type-plugin';  
79 - case types.entityType.tenant:  
80 - return 'entity.type-tenant';  
81 - case types.entityType.customer:  
82 - return 'entity.type-customer';  
83 - } 72 + return utils.entityTypeName(type);
84 } 73 }
85 74
86 scope.updateValidity = function () { 75 scope.updateValidity = function () {
@@ -18,12 +18,15 @@ import EntityAliasesController from './entity-aliases.controller'; @@ -18,12 +18,15 @@ import EntityAliasesController from './entity-aliases.controller';
18 import EntityTypeSelectDirective from './entity-type-select.directive'; 18 import EntityTypeSelectDirective from './entity-type-select.directive';
19 import EntitySubtypeSelectDirective from './entity-subtype-select.directive'; 19 import EntitySubtypeSelectDirective from './entity-subtype-select.directive';
20 import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive'; 20 import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive';
  21 +import EntityAutocompleteDirective from './entity-autocomplete.directive';
  22 +import EntitySelectDirective from './entity-select.directive';
21 import EntityFilterDirective from './entity-filter.directive'; 23 import EntityFilterDirective from './entity-filter.directive';
22 import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller'; 24 import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller';
23 import AliasesEntitySelectDirective from './aliases-entity-select.directive'; 25 import AliasesEntitySelectDirective from './aliases-entity-select.directive';
24 import AddAttributeDialogController from './attribute/add-attribute-dialog.controller'; 26 import AddAttributeDialogController from './attribute/add-attribute-dialog.controller';
25 import AddWidgetToDashboardDialogController from './attribute/add-widget-to-dashboard-dialog.controller'; 27 import AddWidgetToDashboardDialogController from './attribute/add-widget-to-dashboard-dialog.controller';
26 import AttributeTableDirective from './attribute/attribute-table.directive'; 28 import AttributeTableDirective from './attribute/attribute-table.directive';
  29 +import RelationTableDirective from './relation/relation-table.directive';
27 30
28 export default angular.module('thingsboard.entity', []) 31 export default angular.module('thingsboard.entity', [])
29 .controller('EntityAliasesController', EntityAliasesController) 32 .controller('EntityAliasesController', EntityAliasesController)
@@ -33,7 +36,10 @@ export default angular.module('thingsboard.entity', []) @@ -33,7 +36,10 @@ export default angular.module('thingsboard.entity', [])
33 .directive('tbEntityTypeSelect', EntityTypeSelectDirective) 36 .directive('tbEntityTypeSelect', EntityTypeSelectDirective)
34 .directive('tbEntitySubtypeSelect', EntitySubtypeSelectDirective) 37 .directive('tbEntitySubtypeSelect', EntitySubtypeSelectDirective)
35 .directive('tbEntitySubtypeAutocomplete', EntitySubtypeAutocompleteDirective) 38 .directive('tbEntitySubtypeAutocomplete', EntitySubtypeAutocompleteDirective)
  39 + .directive('tbEntityAutocomplete', EntityAutocompleteDirective)
  40 + .directive('tbEntitySelect', EntitySelectDirective)
36 .directive('tbEntityFilter', EntityFilterDirective) 41 .directive('tbEntityFilter', EntityFilterDirective)
37 .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) 42 .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective)
38 .directive('tbAttributeTable', AttributeTableDirective) 43 .directive('tbAttributeTable', AttributeTableDirective)
  44 + .directive('tbRelationTable', RelationTableDirective)
39 .name; 45 .name;
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +/*@ngInject*/
  17 +export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, from) {
  18 +
  19 + var vm = this;
  20 +
  21 + vm.types = types;
  22 +
  23 + vm.relation = {};
  24 + vm.relation.from = from;
  25 + vm.relation.type = types.entityRelationType.contains;
  26 +
  27 + vm.add = add;
  28 + vm.cancel = cancel;
  29 +
  30 + function cancel() {
  31 + $mdDialog.cancel();
  32 + }
  33 +
  34 + function add() {
  35 + $scope.theForm.$setPristine();
  36 + entityRelationService.saveRelation(vm.relation).then(
  37 + function success() {
  38 + $mdDialog.hide();
  39 + }
  40 + );
  41 + }
  42 +
  43 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<md-dialog aria-label="{{ 'relation.add' | translate }}" style="min-width: 400px;">
  19 + <form name="theForm" ng-submit="vm.add()">
  20 + <md-toolbar>
  21 + <div class="md-toolbar-tools">
  22 + <h2 translate>relation.add</h2>
  23 + <span flex></span>
  24 + <md-button class="md-icon-button" ng-click="vm.cancel()">
  25 + <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
  26 + </md-button>
  27 + </div>
  28 + </md-toolbar>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  31 + <md-dialog-content>
  32 + <div class="md-dialog-content">
  33 + <md-content class="md-padding" layout="column">
  34 + <fieldset ng-disabled="loading">
  35 + <md-input-container class="md-block">
  36 + <label translate>relation.relation-type</label>
  37 + <md-select required ng-model="vm.relation.type" ng-disabled="loading">
  38 + <md-option ng-repeat="type in vm.types.entityRelationType" ng-value="type">
  39 + <span>{{('relation.relation-types.' + type) | translate}}</span>
  40 + </md-option>
  41 + </md-select>
  42 + </md-input-container>
  43 + <span class="tb-small">{{'entity.entity' | translate }}</span>
  44 + <tb-entity-select flex
  45 + the-form="theForm"
  46 + tb-required="true"
  47 + ng-model="vm.relation.to">
  48 + </tb-entity-select>
  49 + </fieldset>
  50 + </md-content>
  51 + </div>
  52 + </md-dialog-content>
  53 + <md-dialog-actions layout="row">
  54 + <span flex></span>
  55 + <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
  56 + class="md-raised md-primary">
  57 + {{ 'action.add' | translate }}
  58 + </md-button>
  59 + <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  60 + translate }}
  61 + </md-button>
  62 + </md-dialog-actions>
  63 + </form>
  64 +</md-dialog>
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import 'angular-material-data-table/dist/md-data-table.min.css';
  17 +import './relation-table.scss';
  18 +
  19 +/* eslint-disable import/no-unresolved, import/default */
  20 +
  21 +import relationTableTemplate from './relation-table.tpl.html';
  22 +import addRelationTemplate from './add-relation-dialog.tpl.html';
  23 +
  24 +/* eslint-enable import/no-unresolved, import/default */
  25 +
  26 +import AddRelationController from './add-relation-dialog.controller';
  27 +
  28 +/*@ngInject*/
  29 +export default function RelationTable() {
  30 + return {
  31 + restrict: "E",
  32 + scope: true,
  33 + bindToController: {
  34 + entityId: '=',
  35 + entityType: '@'
  36 + },
  37 + controller: RelationTableController,
  38 + controllerAs: 'vm',
  39 + templateUrl: relationTableTemplate
  40 + };
  41 +}
  42 +
  43 +/*@ngInject*/
  44 +function RelationTableController($scope, $q, $mdDialog, $document, $translate, $filter, utils, types, entityRelationService) {
  45 +
  46 + let vm = this;
  47 +
  48 + vm.relations = [];
  49 + vm.relationsCount = 0;
  50 + vm.allRelations = [];
  51 + vm.selectedRelations = [];
  52 +
  53 + vm.query = {
  54 + order: 'typeName',
  55 + limit: 5,
  56 + page: 1,
  57 + search: null
  58 + };
  59 +
  60 + vm.enterFilterMode = enterFilterMode;
  61 + vm.exitFilterMode = exitFilterMode;
  62 + vm.onReorder = onReorder;
  63 + vm.onPaginate = onPaginate;
  64 + vm.addRelation = addRelation;
  65 + vm.editRelation = editRelation;
  66 + vm.deleteRelation = deleteRelation;
  67 + vm.deleteRelations = deleteRelations;
  68 + vm.reloadRelations = reloadRelations;
  69 + vm.updateRelations = updateRelations;
  70 +
  71 +
  72 + $scope.$watch("vm.entityId", function(newVal, prevVal) {
  73 + if (newVal && !angular.equals(newVal, prevVal)) {
  74 + reloadRelations();
  75 + }
  76 + });
  77 +
  78 + $scope.$watch("vm.query.search", function(newVal, prevVal) {
  79 + if (!angular.equals(newVal, prevVal) && vm.query.search != null) {
  80 + updateRelations();
  81 + }
  82 + });
  83 +
  84 + function enterFilterMode () {
  85 + vm.query.search = '';
  86 + }
  87 +
  88 + function exitFilterMode () {
  89 + vm.query.search = null;
  90 + updateRelations();
  91 + }
  92 +
  93 + function onReorder () {
  94 + updateRelations();
  95 + }
  96 +
  97 + function onPaginate () {
  98 + updateRelations();
  99 + }
  100 +
  101 + function addRelation($event) {
  102 + if ($event) {
  103 + $event.stopPropagation();
  104 + }
  105 + var from = {
  106 + id: vm.entityId,
  107 + entityType: vm.entityType
  108 + };
  109 + $mdDialog.show({
  110 + controller: AddRelationController,
  111 + controllerAs: 'vm',
  112 + templateUrl: addRelationTemplate,
  113 + parent: angular.element($document[0].body),
  114 + locals: { from: from },
  115 + fullscreen: true,
  116 + targetEvent: $event
  117 + }).then(function () {
  118 + reloadRelations();
  119 + }, function () {
  120 + });
  121 + }
  122 +
  123 + function editRelation($event, /*relation*/) {
  124 + if ($event) {
  125 + $event.stopPropagation();
  126 + }
  127 + //TODO:
  128 + }
  129 +
  130 + function deleteRelation($event, /*relation*/) {
  131 + if ($event) {
  132 + $event.stopPropagation();
  133 + }
  134 + //TODO:
  135 + }
  136 +
  137 + function deleteRelations($event) {
  138 + if ($event) {
  139 + $event.stopPropagation();
  140 + }
  141 + //TODO:
  142 + }
  143 +
  144 + function reloadRelations () {
  145 + vm.allRelations.length = 0;
  146 + vm.relations.length = 0;
  147 + vm.relationsPromise = entityRelationService.findInfoByFrom(vm.entityId, vm.entityType);
  148 + vm.relationsPromise.then(
  149 + function success(allRelations) {
  150 + allRelations.forEach(function(relation) {
  151 + relation.typeName = $translate.instant('relation.relation-type.' + relation.type);
  152 + relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType));
  153 + });
  154 + vm.allRelations = allRelations;
  155 + vm.selectedRelations = [];
  156 + vm.updateRelations();
  157 + vm.relationsPromise = null;
  158 + },
  159 + function fail() {
  160 + vm.allRelations = [];
  161 + vm.selectedRelations = [];
  162 + vm.updateRelations();
  163 + vm.relationsPromise = null;
  164 + }
  165 + )
  166 + }
  167 +
  168 + function updateRelations () {
  169 + vm.selectedRelations = [];
  170 + var result = $filter('orderBy')(vm.allRelations, vm.query.order);
  171 + if (vm.query.search != null) {
  172 + result = $filter('filter')(result, {$: vm.query.search});
  173 + }
  174 + vm.relationsCount = result.length;
  175 + var startIndex = vm.query.limit * (vm.query.page - 1);
  176 + vm.relations = result.slice(startIndex, startIndex + vm.query.limit);
  177 + }
  178 +
  179 +}
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +@import '../../../scss/constants';
  17 +
  18 +$md-light: rgba(255, 255, 255, 100%);
  19 +
  20 +.tb-relation-table {
  21 + md-toolbar.md-table-toolbar.alternate {
  22 + .md-toolbar-tools {
  23 + md-icon {
  24 + color: $md-light;
  25 + }
  26 + }
  27 + }
  28 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<md-content flex class="md-padding tb-absolute-fill tb-relation-table tb-data-table" layout="column">
  19 + <div layout="column" class="md-whiteframe-z1">
  20 + <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length
  21 + && vm.query.search === null">
  22 + <div class="md-toolbar-tools">
  23 + <span translate>relation.entity-relations</span>
  24 + <span flex></span>
  25 + <md-button class="md-icon-button" ng-click="vm.addRelation($event)">
  26 + <md-icon>add</md-icon>
  27 + <md-tooltip md-direction="top">
  28 + {{ 'action.add' | translate }}
  29 + </md-tooltip>
  30 + </md-button>
  31 + <md-button class="md-icon-button" ng-click="vm.enterFilterMode()">
  32 + <md-icon>search</md-icon>
  33 + <md-tooltip md-direction="top">
  34 + {{ 'action.search' | translate }}
  35 + </md-tooltip>
  36 + </md-button>
  37 + <md-button class="md-icon-button" ng-click="vm.reloadRelations()">
  38 + <md-icon>refresh</md-icon>
  39 + <md-tooltip md-direction="top">
  40 + {{ 'action.refresh' | translate }}
  41 + </md-tooltip>
  42 + </md-button>
  43 + </div>
  44 + </md-toolbar>
  45 + <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length
  46 + && vm.query.search != null">
  47 + <div class="md-toolbar-tools">
  48 + <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}">
  49 + <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon>
  50 + <md-tooltip md-direction="top">
  51 + {{ 'action.search' | translate }}
  52 + </md-tooltip>
  53 + </md-button>
  54 + <md-input-container flex>
  55 + <label>&nbsp;</label>
  56 + <input ng-model="vm.query.search" placeholder="{{ 'common.enter-search' | translate }}"/>
  57 + </md-input-container>
  58 + <md-button class="md-icon-button" aria-label="{{ 'action.back' | translate }}" ng-click="vm.exitFilterMode()">
  59 + <md-icon aria-label="{{ 'action.close' | translate }}" class="material-icons">close</md-icon>
  60 + <md-tooltip md-direction="top">
  61 + {{ 'action.close' | translate }}
  62 + </md-tooltip>
  63 + </md-button>
  64 + </div>
  65 + </md-toolbar>
  66 + <md-toolbar class="md-table-toolbar alternate" ng-show="vm.selectedRelations.length">
  67 + <div class="md-toolbar-tools">
  68 + <span translate
  69 + translate-values="{count: selectedRelations.length}"
  70 + translate-interpolation="messageformat">relation.selected-relations</span>
  71 + <span flex></span>
  72 + <md-button class="md-icon-button" ng-click="vm.deleteRelations($event)">
  73 + <md-icon>delete</md-icon>
  74 + <md-tooltip md-direction="top">
  75 + {{ 'action.delete' | translate }}
  76 + </md-tooltip>
  77 + </md-button>
  78 + </div>
  79 + </md-toolbar>
  80 + <md-table-container>
  81 + <table md-table md-row-select multiple="" ng-model="vm.selectedRelations" md-progress="vm.relationsDeferred.promise">
  82 + <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder">
  83 + <tr md-row>
  84 + <th md-column md-order-by="typeName"><span translate>relation.type</span></th>
  85 + <th md-column md-order-by="toEntityTypeName"><span translate>relation.to-entity-type</span></th>
  86 + <th md-column md-order-by="toName"><span translate>relation.to-entity-name</span></th>
  87 + <th md-column><span>&nbsp</span></th>
  88 + </tr>
  89 + </thead>
  90 + <tbody md-body>
  91 + <tr md-row md-select="relation" md-select-id="relation" md-auto-select ng-repeat="relation in vm.relations">
  92 + <td md-cell>{{ relation.typeName }}</td>
  93 + <td md-cell>{{ relation.toEntityTypeName }}</td>
  94 + <td md-cell>{{ relation.toName }}</td>
  95 + <td md-cell class="tb-action-cell">
  96 + <md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}"
  97 + ng-click="vm.editRelation($event, relation)">
  98 + <md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon>
  99 + <md-tooltip md-direction="top">
  100 + {{ 'relation.edit' | translate }}
  101 + </md-tooltip>
  102 + </md-button>
  103 + <md-button class="md-icon-button" aria-label="{{ 'action.delete' | translate }}" ng-click="vm.deleteRelation($event, relation)">
  104 + <md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">delete</md-icon>
  105 + <md-tooltip md-direction="top">
  106 + {{ 'relation.delete' | translate }}
  107 + </md-tooltip>
  108 + </md-button>
  109 + </td>
  110 + </tr>
  111 + </tbody>
  112 + </table>
  113 + </md-table-container>
  114 + <md-table-pagination md-limit="vm.query.limit" md-limit-options="[5, 10, 15]"
  115 + md-page="vm.query.page" md-total="{{vm.relationsCount}}"
  116 + md-on-paginate="onPaginate" md-page-select>
  117 + </md-table-pagination>
  118 + </div>
  119 +</md-content>
@@ -235,7 +235,7 @@ @@ -235,7 +235,7 @@
235 "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", 235 "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard",
236 "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", 236 "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard",
237 "select-dashboard": "Seleccionar panel", 237 "select-dashboard": "Seleccionar panel",
238 - "no-dashboards-matching": "Panel '{{dashboard}}' no encontrado.", 238 + "no-dashboards-matching": "Panel '{{entity}}' no encontrado.",
239 "dashboard-required": "Panel requerido.", 239 "dashboard-required": "Panel requerido.",
240 "select-existing": "Seleccionar paneles existentes", 240 "select-existing": "Seleccionar paneles existentes",
241 "create-new": "Crear nuevo panel", 241 "create-new": "Crear nuevo panel",
@@ -330,7 +330,7 @@ @@ -330,7 +330,7 @@
330 "create-new-key": "Crear nueva clave!", 330 "create-new-key": "Crear nueva clave!",
331 "duplicate-alias-error": "Alias duplicado '{{alias}}'.<br> El alias de los dispositivos deben ser únicos dentro del panel.", 331 "duplicate-alias-error": "Alias duplicado '{{alias}}'.<br> El alias de los dispositivos deben ser únicos dentro del panel.",
332 "configure-alias": "Configurar alias '{{alias}}'", 332 "configure-alias": "Configurar alias '{{alias}}'",
333 - "no-devices-matching": "No se encontró dispositivo '{{device}}'", 333 + "no-devices-matching": "No se encontró dispositivo '{{entity}}'",
334 "alias": "Alias", 334 "alias": "Alias",
335 "alias-required": "Alias de dispositivo requerido.", 335 "alias-required": "Alias de dispositivo requerido.",
336 "remove-alias": "Eliminar alias", 336 "remove-alias": "Eliminar alias",
@@ -529,7 +529,7 @@ @@ -529,7 +529,7 @@
529 "system": "Sistema", 529 "system": "Sistema",
530 "select-plugin": "plugin", 530 "select-plugin": "plugin",
531 "plugin": "Plugin", 531 "plugin": "Plugin",
532 - "no-plugins-matching": "No se encontraron plugins: '{{plugin}}'", 532 + "no-plugins-matching": "No se encontraron plugins: '{{entity}}'",
533 "plugin-required": "Plugin requerido.", 533 "plugin-required": "Plugin requerido.",
534 "plugin-require-match": "Por favor, elija un plugin existente.", 534 "plugin-require-match": "Por favor, elija un plugin existente.",
535 "events": "Eventos", 535 "events": "Eventos",
@@ -218,7 +218,7 @@ export default function addLocaleKorean(locales) { @@ -218,7 +218,7 @@ export default function addLocaleKorean(locales) {
218 "unassign-dashboards-title": "{ count, select, 1 {대시보드 1개} other {대시보드 #개} }의 할당을 취소하시겠습니까?", 218 "unassign-dashboards-title": "{ count, select, 1 {대시보드 1개} other {대시보드 #개} }의 할당을 취소하시겠습니까?",
219 "unassign-dashboards-text": "선택된 대시보드가 할당 해제되고 커스터머는 액세스 할 수 없게됩니다.", 219 "unassign-dashboards-text": "선택된 대시보드가 할당 해제되고 커스터머는 액세스 할 수 없게됩니다.",
220 "select-dashboard": "대시보드 선택", 220 "select-dashboard": "대시보드 선택",
221 - "no-dashboards-matching": "'{{dashboard}}'와 일치하는 대시보드가 없습니다.", 221 + "no-dashboards-matching": "'{{entity}}'와 일치하는 대시보드가 없습니다.",
222 "dashboard-required": "대시보드를 입력하세요.", 222 "dashboard-required": "대시보드를 입력하세요.",
223 "select-existing": "기존 대시보드 선택", 223 "select-existing": "기존 대시보드 선택",
224 "create-new": "대시보드 생성", 224 "create-new": "대시보드 생성",
@@ -305,7 +305,7 @@ export default function addLocaleKorean(locales) { @@ -305,7 +305,7 @@ export default function addLocaleKorean(locales) {
305 "create-new-key": "새로 만들기!", 305 "create-new-key": "새로 만들기!",
306 "duplicate-alias-error": "중복된 '{{alias}}' 앨리어스가 있습니다.<br> 디바이스 앨리어스는 대시보드 내에서 고유해야 합니다.", 306 "duplicate-alias-error": "중복된 '{{alias}}' 앨리어스가 있습니다.<br> 디바이스 앨리어스는 대시보드 내에서 고유해야 합니다.",
307 "configure-alias": "'{{alias}}' 앨리어스 구성", 307 "configure-alias": "'{{alias}}' 앨리어스 구성",
308 - "no-devices-matching": "'{{device}}'와 일치하는 디바이스를 찾을 수 없습니다.", 308 + "no-devices-matching": "'{{entity}}'와 일치하는 디바이스를 찾을 수 없습니다.",
309 "alias": "앨리어스", 309 "alias": "앨리어스",
310 "alias-required": "디바이스 앨리어스를 입력하세요.", 310 "alias-required": "디바이스 앨리어스를 입력하세요.",
311 "remove-alias": "디바이스 앨리어스 삭제", 311 "remove-alias": "디바이스 앨리어스 삭제",
@@ -496,7 +496,7 @@ export default function addLocaleKorean(locales) { @@ -496,7 +496,7 @@ export default function addLocaleKorean(locales) {
496 "system": "시스템", 496 "system": "시스템",
497 "select-plugin": "플러그인 선택", 497 "select-plugin": "플러그인 선택",
498 "plugin": "플러그인", 498 "plugin": "플러그인",
499 - "no-plugins-matching": "'{{plugin}}'과 일치하는 플러그인을 찾을 수 없습니다.", 499 + "no-plugins-matching": "'{{entity}}'과 일치하는 플러그인을 찾을 수 없습니다.",
500 "plugin-required": "플러그인을 입력하세요.", 500 "plugin-required": "플러그인을 입력하세요.",
501 "plugin-require-match": "기존의 플러그인을 선택해주세요.", 501 "plugin-require-match": "기존의 플러그인을 선택해주세요.",
502 "events": "이벤트", 502 "events": "이벤트",
@@ -235,7 +235,7 @@ export default function addLocaleRussian(locales) { @@ -235,7 +235,7 @@ export default function addLocaleRussian(locales) {
235 "socialshare-text": "'{{dashboardTitle}}' сделано ThingsBoard", 235 "socialshare-text": "'{{dashboardTitle}}' сделано ThingsBoard",
236 "socialshare-title": "'{{dashboardTitle}}' сделано ThingsBoard", 236 "socialshare-title": "'{{dashboardTitle}}' сделано ThingsBoard",
237 "select-dashboard": "Выберите дашборд", 237 "select-dashboard": "Выберите дашборд",
238 - "no-dashboards-matching": "Дашборд '{{dashboard}}' не найден.", 238 + "no-dashboards-matching": "Дашборд '{{entity}}' не найден.",
239 "dashboard-required": "Дашборд обязателен.", 239 "dashboard-required": "Дашборд обязателен.",
240 "select-existing": "Выберите существующий дашборд", 240 "select-existing": "Выберите существующий дашборд",
241 "create-new": "Создать новый дашборд", 241 "create-new": "Создать новый дашборд",
@@ -330,7 +330,7 @@ export default function addLocaleRussian(locales) { @@ -330,7 +330,7 @@ export default function addLocaleRussian(locales) {
330 "create-new-key": "Создать новый!", 330 "create-new-key": "Создать новый!",
331 "duplicate-alias-error": "Найден дублирующийся псевдоним '{{alias}}'.<br>В рамках дашборда псевдонимы устройств должны быть уникальными.", 331 "duplicate-alias-error": "Найден дублирующийся псевдоним '{{alias}}'.<br>В рамках дашборда псевдонимы устройств должны быть уникальными.",
332 "configure-alias": "Конфигурировать '{{alias}}' псевдоним", 332 "configure-alias": "Конфигурировать '{{alias}}' псевдоним",
333 - "no-devices-matching": "Устройство '{{device}}' не найдено.", 333 + "no-devices-matching": "Устройство '{{entity}}' не найдено.",
334 "alias": "Псевдоним", 334 "alias": "Псевдоним",
335 "alias-required": "Псевдоним устройства обязателен.", 335 "alias-required": "Псевдоним устройства обязателен.",
336 "remove-alias": "Удалить псевдоним устройства", 336 "remove-alias": "Удалить псевдоним устройства",
@@ -529,7 +529,7 @@ export default function addLocaleRussian(locales) { @@ -529,7 +529,7 @@ export default function addLocaleRussian(locales) {
529 "system": "Системный", 529 "system": "Системный",
530 "select-plugin": "Выберите плагин", 530 "select-plugin": "Выберите плагин",
531 "plugin": "Плагин", 531 "plugin": "Плагин",
532 - "no-plugins-matching": "Плагин '{{plugin}}' не найден.", 532 + "no-plugins-matching": "Плагин '{{entity}}' не найден.",
533 "plugin-required": "Плагин обязателен.", 533 "plugin-required": "Плагин обязателен.",
534 "plugin-require-match": "Пожалуйста, выберите существующий плагин.", 534 "plugin-require-match": "Пожалуйста, выберите существующий плагин.",
535 "events": "События", 535 "events": "События",
@@ -235,7 +235,7 @@ export default function addLocaleChinese(locales) { @@ -235,7 +235,7 @@ export default function addLocaleChinese(locales) {
235 "socialshare-text" : "'{{dashboardTitle}}' 由ThingsBoard提供支持", 235 "socialshare-text" : "'{{dashboardTitle}}' 由ThingsBoard提供支持",
236 "socialshare-title" : "'{{dashboardTitle}}' 由ThingsBoard提供支持", 236 "socialshare-title" : "'{{dashboardTitle}}' 由ThingsBoard提供支持",
237 "select-dashboard" : "选择仪表板", 237 "select-dashboard" : "选择仪表板",
238 - "no-dashboards-matching" : "找不到符合 '{{dashboard}}' 的仪表板。", 238 + "no-dashboards-matching" : "找不到符合 '{{entity}}' 的仪表板。",
239 "dashboard-required" : "仪表板是必需的。", 239 "dashboard-required" : "仪表板是必需的。",
240 "select-existing" : "选择现有仪表板", 240 "select-existing" : "选择现有仪表板",
241 "create-new" : "创建新的仪表板", 241 "create-new" : "创建新的仪表板",
@@ -330,7 +330,7 @@ export default function addLocaleChinese(locales) { @@ -330,7 +330,7 @@ export default function addLocaleChinese(locales) {
330 "create-new-key": "创建一个新的!", 330 "create-new-key": "创建一个新的!",
331 "duplicate-alias-error" : "找到重复别名 '{{alias}}'。 <br> 设备别名必须是唯一的。", 331 "duplicate-alias-error" : "找到重复别名 '{{alias}}'。 <br> 设备别名必须是唯一的。",
332 "configure-alias" : "配置 '{{alias}}' 别名", 332 "configure-alias" : "配置 '{{alias}}' 别名",
333 - "no-devices-matching" : "找不到与 '{{device}}' 匹配的设备。", 333 + "no-devices-matching" : "找不到与 '{{entity}}' 匹配的设备。",
334 "alias" : "别名", 334 "alias" : "别名",
335 "alias-required" : "需要设备别名。", 335 "alias-required" : "需要设备别名。",
336 "remove-alias": "删除设备别名", 336 "remove-alias": "删除设备别名",
@@ -529,7 +529,7 @@ export default function addLocaleChinese(locales) { @@ -529,7 +529,7 @@ export default function addLocaleChinese(locales) {
529 "system" : "系统", 529 "system" : "系统",
530 "select-plugin" : "选择插件", 530 "select-plugin" : "选择插件",
531 "plugin" : "插件", 531 "plugin" : "插件",
532 - "no-plugins-matching" : "没有找到匹配'{{plugin}}'的插件。", 532 + "no-plugins-matching" : "没有找到匹配'{{entity}}'的插件。",
533 "plugin-required" : "插件是必需的。", 533 "plugin-required" : "插件是必需的。",
534 "plugin-require-match" : "请选择一个现有的插件。", 534 "plugin-require-match" : "请选择一个现有的插件。",
535 "events" : "事件", 535 "events" : "事件",
@@ -106,6 +106,12 @@ export default angular.module('thingsboard.locale', []) @@ -106,6 +106,12 @@ export default angular.module('thingsboard.locale', [])
106 "enable-tls": "Enable TLS", 106 "enable-tls": "Enable TLS",
107 "send-test-mail": "Send test mail" 107 "send-test-mail": "Send test mail"
108 }, 108 },
  109 + "alarm": {
  110 + "alarm": "Alarm",
  111 + "select-alarm": "Select alarm",
  112 + "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
  113 + "alarm-required": "Alarm is required"
  114 + },
109 "asset": { 115 "asset": {
110 "asset": "Asset", 116 "asset": "Asset",
111 "assets": "Assets", 117 "assets": "Assets",
@@ -157,7 +163,10 @@ export default angular.module('thingsboard.locale', []) @@ -157,7 +163,10 @@ export default angular.module('thingsboard.locale', [])
157 "unassign-assets-title": "Are you sure you want to unassign { count, select, 1 {1 asset} other {# assets} }?", 163 "unassign-assets-title": "Are you sure you want to unassign { count, select, 1 {1 asset} other {# assets} }?",
158 "unassign-assets-text": "After the confirmation all selected assets will be unassigned and won't be accessible by the customer.", 164 "unassign-assets-text": "After the confirmation all selected assets will be unassigned and won't be accessible by the customer.",
159 "copyId": "Copy asset Id", 165 "copyId": "Copy asset Id",
160 - "idCopiedMessage": "Asset Id has been copied to clipboard" 166 + "idCopiedMessage": "Asset Id has been copied to clipboard",
  167 + "select-asset": "Select asset",
  168 + "no-assets-matching": "No assets matching '{{entity}}' were found.",
  169 + "asset-required": "Asset is required"
161 }, 170 },
162 "attribute": { 171 "attribute": {
163 "attributes": "Attributes", 172 "attributes": "Attributes",
@@ -169,6 +178,7 @@ export default angular.module('thingsboard.locale', []) @@ -169,6 +178,7 @@ export default angular.module('thingsboard.locale', [])
169 "scope-shared": "Shared attributes", 178 "scope-shared": "Shared attributes",
170 "add": "Add attribute", 179 "add": "Add attribute",
171 "key": "Key", 180 "key": "Key",
  181 + "last-update-time": "Last update time",
172 "key-required": "Attribute key is required.", 182 "key-required": "Attribute key is required.",
173 "value": "Value", 183 "value": "Value",
174 "value-required": "Attribute value is required.", 184 "value-required": "Attribute value is required.",
@@ -210,6 +220,7 @@ export default angular.module('thingsboard.locale', []) @@ -210,6 +220,7 @@ export default angular.module('thingsboard.locale', [])
210 "enter-search": "Enter search" 220 "enter-search": "Enter search"
211 }, 221 },
212 "customer": { 222 "customer": {
  223 + "customer": "Customer",
213 "customers": "Customers", 224 "customers": "Customers",
214 "management": "Customer management", 225 "management": "Customer management",
215 "dashboard": "Customer Dashboard", 226 "dashboard": "Customer Dashboard",
@@ -246,7 +257,10 @@ export default angular.module('thingsboard.locale', []) @@ -246,7 +257,10 @@ export default angular.module('thingsboard.locale', [])
246 "details": "Details", 257 "details": "Details",
247 "events": "Events", 258 "events": "Events",
248 "copyId": "Copy customer Id", 259 "copyId": "Copy customer Id",
249 - "idCopiedMessage": "Customer Id has been copied to clipboard" 260 + "idCopiedMessage": "Customer Id has been copied to clipboard",
  261 + "select-customer": "Select customer",
  262 + "no-customers-matching": "No customers matching '{{entity}}' were found.",
  263 + "customer-required": "Customer is required"
250 }, 264 },
251 "datetime": { 265 "datetime": {
252 "date-from": "Date from", 266 "date-from": "Date from",
@@ -304,7 +318,7 @@ export default angular.module('thingsboard.locale', []) @@ -304,7 +318,7 @@ export default angular.module('thingsboard.locale', [])
304 "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", 318 "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard",
305 "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", 319 "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard",
306 "select-dashboard": "Select dashboard", 320 "select-dashboard": "Select dashboard",
307 - "no-dashboards-matching": "No dashboards matching '{{dashboard}}' were found.", 321 + "no-dashboards-matching": "No dashboards matching '{{entity}}' were found.",
308 "dashboard-required": "Dashboard is required.", 322 "dashboard-required": "Dashboard is required.",
309 "select-existing": "Select existing dashboard", 323 "select-existing": "Select existing dashboard",
310 "create-new": "Create new dashboard", 324 "create-new": "Create new dashboard",
@@ -425,7 +439,7 @@ export default angular.module('thingsboard.locale', []) @@ -425,7 +439,7 @@ export default angular.module('thingsboard.locale', [])
425 "create-new-key": "Create a new one!", 439 "create-new-key": "Create a new one!",
426 "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Device aliases must be unique whithin the dashboard.", 440 "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Device aliases must be unique whithin the dashboard.",
427 "configure-alias": "Configure '{{alias}}' alias", 441 "configure-alias": "Configure '{{alias}}' alias",
428 - "no-devices-matching": "No devices matching '{{device}}' were found.", 442 + "no-devices-matching": "No devices matching '{{entity}}' were found.",
429 "alias": "Alias", 443 "alias": "Alias",
430 "alias-required": "Device alias is required.", 444 "alias-required": "Device alias is required.",
431 "remove-alias": "Remove device alias", 445 "remove-alias": "Remove device alias",
@@ -497,7 +511,8 @@ export default angular.module('thingsboard.locale', []) @@ -497,7 +511,8 @@ export default angular.module('thingsboard.locale', [])
497 "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}", 511 "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
498 "is-gateway": "Is gateway", 512 "is-gateway": "Is gateway",
499 "public": "Public", 513 "public": "Public",
500 - "device-public": "Device is public" 514 + "device-public": "Device is public",
  515 + "select-device": "Select device"
501 }, 516 },
502 "dialog": { 517 "dialog": {
503 "close": "Close dialog" 518 "close": "Close dialog"
@@ -535,6 +550,9 @@ export default angular.module('thingsboard.locale', []) @@ -535,6 +550,9 @@ export default angular.module('thingsboard.locale', [])
535 "type-plugin": "Plugin", 550 "type-plugin": "Plugin",
536 "type-tenant": "Tenant", 551 "type-tenant": "Tenant",
537 "type-customer": "Customer", 552 "type-customer": "Customer",
  553 + "type-user": "User",
  554 + "type-dashboard": "Dashboard",
  555 + "type-alarm": "Alarm",
538 "select-entities": "Select entities", 556 "select-entities": "Select entities",
539 "no-aliases-found": "No aliases found.", 557 "no-aliases-found": "No aliases found.",
540 "no-alias-matching": "'{{alias}}' not found.", 558 "no-alias-matching": "'{{alias}}' not found.",
@@ -672,7 +690,7 @@ export default angular.module('thingsboard.locale', []) @@ -672,7 +690,7 @@ export default angular.module('thingsboard.locale', [])
672 "system": "System", 690 "system": "System",
673 "select-plugin": "Select plugin", 691 "select-plugin": "Select plugin",
674 "plugin": "Plugin", 692 "plugin": "Plugin",
675 - "no-plugins-matching": "No plugins matching '{{plugin}}' were found.", 693 + "no-plugins-matching": "No plugins matching '{{entity}}' were found.",
676 "plugin-required": "Plugin is required.", 694 "plugin-required": "Plugin is required.",
677 "plugin-require-match": "Please select an existing plugin.", 695 "plugin-require-match": "Please select an existing plugin.",
678 "events": "Events", 696 "events": "Events",
@@ -685,6 +703,7 @@ export default angular.module('thingsboard.locale', []) @@ -685,6 +703,7 @@ export default angular.module('thingsboard.locale', [])
685 "invalid-plugin-file-error": "Unable to import plugin: Invalid plugin data structure.", 703 "invalid-plugin-file-error": "Unable to import plugin: Invalid plugin data structure.",
686 "copyId": "Copy plugin Id", 704 "copyId": "Copy plugin Id",
687 "idCopiedMessage": "Plugin Id has been copied to clipboard" 705 "idCopiedMessage": "Plugin Id has been copied to clipboard"
  706 +
688 }, 707 },
689 "position": { 708 "position": {
690 "top": "Top", 709 "top": "Top",
@@ -697,7 +716,24 @@ export default angular.module('thingsboard.locale', []) @@ -697,7 +716,24 @@ export default angular.module('thingsboard.locale', [])
697 "change-password": "Change Password", 716 "change-password": "Change Password",
698 "current-password": "Current password" 717 "current-password": "Current password"
699 }, 718 },
  719 + "relation": {
  720 + "relations": "Relations",
  721 + "entity-relations": "Entity relations",
  722 + "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected",
  723 + "type": "Type",
  724 + "to-entity-type": "Entity type",
  725 + "to-entity-name": "Entity name",
  726 + "edit": "Edit relation",
  727 + "delete": "Delete relation",
  728 + "relation-type": "Relation type",
  729 + "relation-types": {
  730 + "Contains": "Contains",
  731 + "Manages": "Manages"
  732 + },
  733 + "add": "Add relation"
  734 + },
700 "rule": { 735 "rule": {
  736 + "rule": "Rule",
701 "rules": "Rules", 737 "rules": "Rules",
702 "delete": "Delete rule", 738 "delete": "Delete rule",
703 "activate": "Activate rule", 739 "activate": "Activate rule",
@@ -749,12 +785,16 @@ export default angular.module('thingsboard.locale', []) @@ -749,12 +785,16 @@ export default angular.module('thingsboard.locale', [])
749 "rule-file": "Rule file", 785 "rule-file": "Rule file",
750 "invalid-rule-file-error": "Unable to import rule: Invalid rule data structure.", 786 "invalid-rule-file-error": "Unable to import rule: Invalid rule data structure.",
751 "copyId": "Copy rule Id", 787 "copyId": "Copy rule Id",
752 - "idCopiedMessage": "Rule Id has been copied to clipboard" 788 + "idCopiedMessage": "Rule Id has been copied to clipboard",
  789 + "select-rule": "Select rule",
  790 + "no-rules-matching": "No rules matching '{{entity}}' were found.",
  791 + "rule-required": "Rule is required"
753 }, 792 },
754 "rule-plugin": { 793 "rule-plugin": {
755 "management": "Rules and plugins management" 794 "management": "Rules and plugins management"
756 }, 795 },
757 "tenant": { 796 "tenant": {
  797 + "tenant": "Tenant",
758 "tenants": "Tenants", 798 "tenants": "Tenants",
759 "management": "Tenant management", 799 "management": "Tenant management",
760 "add": "Add Tenant", 800 "add": "Add Tenant",
@@ -775,7 +815,10 @@ export default angular.module('thingsboard.locale', []) @@ -775,7 +815,10 @@ export default angular.module('thingsboard.locale', [])
775 "details": "Details", 815 "details": "Details",
776 "events": "Events", 816 "events": "Events",
777 "copyId": "Copy tenant Id", 817 "copyId": "Copy tenant Id",
778 - "idCopiedMessage": "Tenant Id has been copied to clipboard" 818 + "idCopiedMessage": "Tenant Id has been copied to clipboard",
  819 + "select-tenant": "Select tenant",
  820 + "no-tenants-matching": "No tenants matching '{{entity}}' were found.",
  821 + "tenant-required": "Tenant is required"
779 }, 822 },
780 "timeinterval": { 823 "timeinterval": {
781 "seconds-interval": "{ seconds, select, 1 {1 second} other {# seconds} }", 824 "seconds-interval": "{ seconds, select, 1 {1 second} other {# seconds} }",
@@ -803,6 +846,7 @@ export default angular.module('thingsboard.locale', []) @@ -803,6 +846,7 @@ export default angular.module('thingsboard.locale', [])
803 "time-period": "Time period" 846 "time-period": "Time period"
804 }, 847 },
805 "user": { 848 "user": {
  849 + "user": "User",
806 "users": "Users", 850 "users": "Users",
807 "customer-users": "Customer Users", 851 "customer-users": "Customer Users",
808 "tenant-admins": "Tenant Admins", 852 "tenant-admins": "Tenant Admins",
@@ -828,7 +872,10 @@ export default angular.module('thingsboard.locale', []) @@ -828,7 +872,10 @@ export default angular.module('thingsboard.locale', [])
828 "last-name": "Last Name", 872 "last-name": "Last Name",
829 "description": "Description", 873 "description": "Description",
830 "default-dashboard": "Default dashboard", 874 "default-dashboard": "Default dashboard",
831 - "always-fullscreen": "Always fullscreen" 875 + "always-fullscreen": "Always fullscreen",
  876 + "select-user": "Select user",
  877 + "no-users-matching": "No users matching '{{entity}}' were found.",
  878 + "user-required": "User is required"
832 }, 879 },
833 "value": { 880 "value": {
834 "type": "Value type", 881 "type": "Value type",
@@ -261,6 +261,45 @@ pre.tb-highlight { @@ -261,6 +261,45 @@ pre.tb-highlight {
261 font-size: 16px; 261 font-size: 16px;
262 } 262 }
263 263
  264 +.tb-data-table {
  265 + md-toolbar {
  266 + z-index: 0;
  267 + }
  268 + span.no-data-found {
  269 + position: relative;
  270 + height: calc(100% - 57px);
  271 + text-transform: uppercase;
  272 + display: flex;
  273 + }
  274 + table.md-table {
  275 + tbody {
  276 + tr {
  277 + td {
  278 + &.tb-action-cell {
  279 + overflow: hidden;
  280 + text-overflow: ellipsis;
  281 + white-space: nowrap;
  282 + min-width: 72px;
  283 + max-width: 72px;
  284 + width: 72px;
  285 + .md-button {
  286 + &.md-icon-button {
  287 + margin: 0;
  288 + padding: 6px;
  289 + width: 36px;
  290 + height: 36px;
  291 + }
  292 + }
  293 + .tb-spacer {
  294 + padding-left: 38px;
  295 + }
  296 + }
  297 + }
  298 + }
  299 + }
  300 + }
  301 +}
  302 +
264 303
265 /*********************** 304 /***********************
266 * Flow 305 * Flow