Commit ecb5c50d61add5a92c33ee91afef5244109e8480
1 parent
3c43d039
UI: Implement Entity relations management.
Showing
24 changed files
with
454 additions
and
94 deletions
@@ -34,7 +34,7 @@ import java.util.List; | @@ -34,7 +34,7 @@ import java.util.List; | ||
34 | @RequestMapping("/api") | 34 | @RequestMapping("/api") |
35 | public class EntityRelationController extends BaseController { | 35 | public class EntityRelationController extends BaseController { |
36 | 36 | ||
37 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 37 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
38 | @RequestMapping(value = "/relation", method = RequestMethod.POST) | 38 | @RequestMapping(value = "/relation", method = RequestMethod.POST) |
39 | @ResponseStatus(value = HttpStatus.OK) | 39 | @ResponseStatus(value = HttpStatus.OK) |
40 | public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException { | 40 | public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException { |
@@ -42,31 +42,33 @@ public class EntityRelationController extends BaseController { | @@ -42,31 +42,33 @@ public class EntityRelationController extends BaseController { | ||
42 | checkNotNull(relation); | 42 | checkNotNull(relation); |
43 | checkEntityId(relation.getFrom()); | 43 | checkEntityId(relation.getFrom()); |
44 | checkEntityId(relation.getTo()); | 44 | checkEntityId(relation.getTo()); |
45 | + if (relation.getTypeGroup() == null) { | ||
46 | + relation.setTypeGroup(RelationTypeGroup.COMMON); | ||
47 | + } | ||
45 | relationService.saveRelation(relation).get(); | 48 | relationService.saveRelation(relation).get(); |
46 | } catch (Exception e) { | 49 | } catch (Exception e) { |
47 | throw handleException(e); | 50 | throw handleException(e); |
48 | } | 51 | } |
49 | } | 52 | } |
50 | 53 | ||
51 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 54 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
52 | @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {"fromId", "fromType", "relationType", "toId", "toType"}) | 55 | @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {"fromId", "fromType", "relationType", "toId", "toType"}) |
53 | @ResponseStatus(value = HttpStatus.OK) | 56 | @ResponseStatus(value = HttpStatus.OK) |
54 | public void deleteRelation(@RequestParam("fromId") String strFromId, | 57 | public void deleteRelation(@RequestParam("fromId") String strFromId, |
55 | @RequestParam("fromType") String strFromType, | 58 | @RequestParam("fromType") String strFromType, |
56 | @RequestParam("relationType") String strRelationType, | 59 | @RequestParam("relationType") String strRelationType, |
57 | - @RequestParam("relationTypeGroup") String strRelationTypeGroup, | 60 | + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup, |
58 | @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException { | 61 | @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException { |
59 | checkParameter("fromId", strFromId); | 62 | checkParameter("fromId", strFromId); |
60 | checkParameter("fromType", strFromType); | 63 | checkParameter("fromType", strFromType); |
61 | checkParameter("relationType", strRelationType); | 64 | checkParameter("relationType", strRelationType); |
62 | - checkParameter("relationTypeGroup", strRelationTypeGroup); | ||
63 | checkParameter("toId", strToId); | 65 | checkParameter("toId", strToId); |
64 | checkParameter("toType", strToType); | 66 | checkParameter("toType", strToType); |
65 | EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); | 67 | EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId); |
66 | EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); | 68 | EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId); |
67 | checkEntityId(fromId); | 69 | checkEntityId(fromId); |
68 | checkEntityId(toId); | 70 | checkEntityId(toId); |
69 | - RelationTypeGroup relationTypeGroup = RelationTypeGroup.valueOf(strRelationTypeGroup); | 71 | + RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); |
70 | try { | 72 | try { |
71 | Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup).get(); | 73 | Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup).get(); |
72 | if (!found) { | 74 | if (!found) { |
@@ -77,7 +79,7 @@ public class EntityRelationController extends BaseController { | @@ -77,7 +79,7 @@ public class EntityRelationController extends BaseController { | ||
77 | } | 79 | } |
78 | } | 80 | } |
79 | 81 | ||
80 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 82 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") |
81 | @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"id", "type"}) | 83 | @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"id", "type"}) |
82 | @ResponseStatus(value = HttpStatus.OK) | 84 | @ResponseStatus(value = HttpStatus.OK) |
83 | public void deleteRelations(@RequestParam("entityId") String strId, | 85 | public void deleteRelations(@RequestParam("entityId") String strId, |
@@ -93,7 +95,7 @@ public class EntityRelationController extends BaseController { | @@ -93,7 +95,7 @@ public class EntityRelationController extends BaseController { | ||
93 | } | 95 | } |
94 | } | 96 | } |
95 | 97 | ||
96 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 98 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
97 | @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType", "toId", "toType"}) | 99 | @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType", "toId", "toType"}) |
98 | @ResponseStatus(value = HttpStatus.OK) | 100 | @ResponseStatus(value = HttpStatus.OK) |
99 | public void checkRelation(@RequestParam("fromId") String strFromId, | 101 | public void checkRelation(@RequestParam("fromId") String strFromId, |
@@ -121,7 +123,7 @@ public class EntityRelationController extends BaseController { | @@ -121,7 +123,7 @@ public class EntityRelationController extends BaseController { | ||
121 | } | 123 | } |
122 | } | 124 | } |
123 | 125 | ||
124 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 126 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
125 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType"}) | 127 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType"}) |
126 | @ResponseBody | 128 | @ResponseBody |
127 | public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, | 129 | public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, |
@@ -139,7 +141,7 @@ public class EntityRelationController extends BaseController { | @@ -139,7 +141,7 @@ public class EntityRelationController extends BaseController { | ||
139 | } | 141 | } |
140 | } | 142 | } |
141 | 143 | ||
142 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 144 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
143 | @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"fromId", "fromType"}) | 145 | @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"fromId", "fromType"}) |
144 | @ResponseBody | 146 | @ResponseBody |
145 | public List<EntityRelationInfo> findInfoByFrom(@RequestParam("fromId") String strFromId, | 147 | public List<EntityRelationInfo> findInfoByFrom(@RequestParam("fromId") String strFromId, |
@@ -157,7 +159,7 @@ public class EntityRelationController extends BaseController { | @@ -157,7 +159,7 @@ public class EntityRelationController extends BaseController { | ||
157 | } | 159 | } |
158 | } | 160 | } |
159 | 161 | ||
160 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 162 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
161 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"}) | 163 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"}) |
162 | @ResponseBody | 164 | @ResponseBody |
163 | public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, | 165 | public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, |
@@ -177,7 +179,7 @@ public class EntityRelationController extends BaseController { | @@ -177,7 +179,7 @@ public class EntityRelationController extends BaseController { | ||
177 | } | 179 | } |
178 | } | 180 | } |
179 | 181 | ||
180 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 182 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
181 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType"}) | 183 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType"}) |
182 | @ResponseBody | 184 | @ResponseBody |
183 | public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, | 185 | public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, |
@@ -195,7 +197,25 @@ public class EntityRelationController extends BaseController { | @@ -195,7 +197,25 @@ public class EntityRelationController extends BaseController { | ||
195 | } | 197 | } |
196 | } | 198 | } |
197 | 199 | ||
198 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 200 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
201 | + @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"toId", "toType"}) | ||
202 | + @ResponseBody | ||
203 | + public List<EntityRelationInfo> findInfoByTo(@RequestParam("toId") String strToId, | ||
204 | + @RequestParam("toType") String strToType, | ||
205 | + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { | ||
206 | + checkParameter("toId", strToId); | ||
207 | + checkParameter("toType", strToType); | ||
208 | + EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); | ||
209 | + checkEntityId(entityId); | ||
210 | + RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); | ||
211 | + try { | ||
212 | + return checkNotNull(relationService.findInfoByTo(entityId, typeGroup).get()); | ||
213 | + } catch (Exception e) { | ||
214 | + throw handleException(e); | ||
215 | + } | ||
216 | + } | ||
217 | + | ||
218 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | ||
199 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType", "relationType"}) | 219 | @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType", "relationType"}) |
200 | @ResponseBody | 220 | @ResponseBody |
201 | public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, | 221 | public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, |
@@ -215,7 +235,7 @@ public class EntityRelationController extends BaseController { | @@ -215,7 +235,7 @@ public class EntityRelationController extends BaseController { | ||
215 | } | 235 | } |
216 | } | 236 | } |
217 | 237 | ||
218 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 238 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
219 | @RequestMapping(value = "/relations", method = RequestMethod.POST) | 239 | @RequestMapping(value = "/relations", method = RequestMethod.POST) |
220 | @ResponseBody | 240 | @ResponseBody |
221 | public List<EntityRelation> findByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException { | 241 | public List<EntityRelation> findByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException { |
@@ -20,6 +20,7 @@ public class EntityRelationInfo extends EntityRelation { | @@ -20,6 +20,7 @@ public class EntityRelationInfo extends EntityRelation { | ||
20 | 20 | ||
21 | private static final long serialVersionUID = 2807343097519543363L; | 21 | private static final long serialVersionUID = 2807343097519543363L; |
22 | 22 | ||
23 | + private String fromName; | ||
23 | private String toName; | 24 | private String toName; |
24 | 25 | ||
25 | public EntityRelationInfo() { | 26 | public EntityRelationInfo() { |
@@ -30,6 +31,14 @@ public class EntityRelationInfo extends EntityRelation { | @@ -30,6 +31,14 @@ public class EntityRelationInfo extends EntityRelation { | ||
30 | super(entityRelation); | 31 | super(entityRelation); |
31 | } | 32 | } |
32 | 33 | ||
34 | + public String getFromName() { | ||
35 | + return fromName; | ||
36 | + } | ||
37 | + | ||
38 | + public void setFromName(String fromName) { | ||
39 | + this.fromName = fromName; | ||
40 | + } | ||
41 | + | ||
33 | public String getToName() { | 42 | public String getToName() { |
34 | return toName; | 43 | return toName; |
35 | } | 44 | } |
@@ -23,29 +23,17 @@ import lombok.extern.slf4j.Slf4j; | @@ -23,29 +23,17 @@ 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; | ||
32 | import org.thingsboard.server.common.data.id.EntityId; | 26 | import org.thingsboard.server.common.data.id.EntityId; |
33 | -import org.thingsboard.server.common.data.id.UUIDBased; | ||
34 | import org.thingsboard.server.common.data.relation.EntityRelation; | 27 | import org.thingsboard.server.common.data.relation.EntityRelation; |
35 | import org.thingsboard.server.common.data.relation.EntityRelationInfo; | 28 | import org.thingsboard.server.common.data.relation.EntityRelationInfo; |
36 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; | 29 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
37 | -import org.thingsboard.server.dao.asset.AssetService; | ||
38 | -import org.thingsboard.server.dao.customer.CustomerService; | ||
39 | -import org.thingsboard.server.dao.device.DeviceService; | ||
40 | import org.thingsboard.server.dao.entity.EntityService; | 30 | import org.thingsboard.server.dao.entity.EntityService; |
41 | import org.thingsboard.server.dao.exception.DataValidationException; | 31 | import org.thingsboard.server.dao.exception.DataValidationException; |
42 | -import org.thingsboard.server.dao.plugin.PluginService; | ||
43 | -import org.thingsboard.server.dao.rule.RuleService; | ||
44 | -import org.thingsboard.server.dao.tenant.TenantService; | ||
45 | 32 | ||
46 | import javax.annotation.Nullable; | 33 | import javax.annotation.Nullable; |
47 | import java.util.*; | 34 | import java.util.*; |
48 | import java.util.concurrent.ConcurrentHashMap; | 35 | import java.util.concurrent.ConcurrentHashMap; |
36 | +import java.util.function.BiConsumer; | ||
49 | 37 | ||
50 | /** | 38 | /** |
51 | * Created by ashvayka on 28.04.17. | 39 | * Created by ashvayka on 28.04.17. |
@@ -133,23 +121,16 @@ public class BaseRelationService implements RelationService { | @@ -133,23 +121,16 @@ public class BaseRelationService implements RelationService { | ||
133 | ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, | 121 | ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, |
134 | (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { | 122 | (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { |
135 | List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); | 123 | List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); |
136 | - relations1.stream().forEach(relation -> futures.add(fetchRelationInfoAsync(relation))); | ||
137 | - return Futures.successfulAsList(futures); | 124 | + relations1.stream().forEach(relation -> |
125 | + futures.add(fetchRelationInfoAsync(relation, | ||
126 | + relation2 -> relation2.getTo(), | ||
127 | + (EntityRelationInfo relationInfo, String entityName) -> relationInfo.setToName(entityName))) | ||
128 | + ); | ||
129 | + return Futures.successfulAsList(futures); | ||
138 | }); | 130 | }); |
139 | return relationsInfo; | 131 | return relationsInfo; |
140 | } | 132 | } |
141 | 133 | ||
142 | - private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation) { | ||
143 | - ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(relation.getTo()); | ||
144 | - ListenableFuture<EntityRelationInfo> entityRelationInfo = | ||
145 | - Futures.transform(entityName, (Function<String, EntityRelationInfo>) entityName1 -> { | ||
146 | - EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation); | ||
147 | - entityRelationInfo1.setToName(entityName1); | ||
148 | - return entityRelationInfo1; | ||
149 | - }); | ||
150 | - return entityRelationInfo; | ||
151 | - } | ||
152 | - | ||
153 | @Override | 134 | @Override |
154 | public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { | 135 | public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { |
155 | log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup); | 136 | log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup); |
@@ -168,6 +149,38 @@ public class BaseRelationService implements RelationService { | @@ -168,6 +149,38 @@ public class BaseRelationService implements RelationService { | ||
168 | } | 149 | } |
169 | 150 | ||
170 | @Override | 151 | @Override |
152 | + public ListenableFuture<List<EntityRelationInfo>> findInfoByTo(EntityId to, RelationTypeGroup typeGroup) { | ||
153 | + log.trace("Executing findInfoByTo [{}][{}]", to, typeGroup); | ||
154 | + validate(to); | ||
155 | + validateTypeGroup(typeGroup); | ||
156 | + ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByTo(to, typeGroup); | ||
157 | + ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, | ||
158 | + (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { | ||
159 | + List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); | ||
160 | + relations1.stream().forEach(relation -> | ||
161 | + futures.add(fetchRelationInfoAsync(relation, | ||
162 | + relation2 -> relation2.getFrom(), | ||
163 | + (EntityRelationInfo relationInfo, String entityName) -> relationInfo.setFromName(entityName))) | ||
164 | + ); | ||
165 | + return Futures.successfulAsList(futures); | ||
166 | + }); | ||
167 | + return relationsInfo; | ||
168 | + } | ||
169 | + | ||
170 | + private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation, | ||
171 | + Function<EntityRelation, EntityId> entityIdGetter, | ||
172 | + BiConsumer<EntityRelationInfo, String> entityNameSetter) { | ||
173 | + ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(entityIdGetter.apply(relation)); | ||
174 | + ListenableFuture<EntityRelationInfo> entityRelationInfo = | ||
175 | + Futures.transform(entityName, (Function<String, EntityRelationInfo>) entityName1 -> { | ||
176 | + EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation); | ||
177 | + entityNameSetter.accept(entityRelationInfo1, entityName1); | ||
178 | + return entityRelationInfo1; | ||
179 | + }); | ||
180 | + return entityRelationInfo; | ||
181 | + } | ||
182 | + | ||
183 | + @Override | ||
171 | public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { | 184 | public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { |
172 | log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup); | 185 | log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup); |
173 | validate(to); | 186 | validate(to); |
@@ -46,6 +46,8 @@ public interface RelationService { | @@ -46,6 +46,8 @@ public interface RelationService { | ||
46 | 46 | ||
47 | ListenableFuture<List<EntityRelation>> findByTo(EntityId to, RelationTypeGroup typeGroup); | 47 | ListenableFuture<List<EntityRelation>> findByTo(EntityId to, RelationTypeGroup typeGroup); |
48 | 48 | ||
49 | + ListenableFuture<List<EntityRelationInfo>> findInfoByTo(EntityId to, RelationTypeGroup typeGroup); | ||
50 | + | ||
49 | ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup); | 51 | ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup); |
50 | 52 | ||
51 | ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query); | 53 | ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query); |
@@ -28,6 +28,7 @@ function EntityRelationService($http, $q) { | @@ -28,6 +28,7 @@ function EntityRelationService($http, $q) { | ||
28 | findInfoByFrom: findInfoByFrom, | 28 | findInfoByFrom: findInfoByFrom, |
29 | findByFromAndType: findByFromAndType, | 29 | findByFromAndType: findByFromAndType, |
30 | findByTo: findByTo, | 30 | findByTo: findByTo, |
31 | + findInfoByTo: findInfoByTo, | ||
31 | findByToAndType: findByToAndType, | 32 | findByToAndType: findByToAndType, |
32 | findByQuery: findByQuery | 33 | findByQuery: findByQuery |
33 | } | 34 | } |
@@ -122,6 +123,18 @@ function EntityRelationService($http, $q) { | @@ -122,6 +123,18 @@ function EntityRelationService($http, $q) { | ||
122 | return deferred.promise; | 123 | return deferred.promise; |
123 | } | 124 | } |
124 | 125 | ||
126 | + function findInfoByTo(toId, toType) { | ||
127 | + var deferred = $q.defer(); | ||
128 | + var url = '/api/relations/info?toId=' + toId; | ||
129 | + url += '&toType=' + toType; | ||
130 | + $http.get(url, null).then(function success(response) { | ||
131 | + deferred.resolve(response.data); | ||
132 | + }, function fail() { | ||
133 | + deferred.reject(); | ||
134 | + }); | ||
135 | + return deferred.promise; | ||
136 | + } | ||
137 | + | ||
125 | function findByToAndType(toId, toType, relationType) { | 138 | function findByToAndType(toId, toType, relationType) { |
126 | var deferred = $q.defer(); | 139 | var deferred = $q.defer(); |
127 | var url = '/api/relations?toId=' + toId; | 140 | var url = '/api/relations?toId=' + toId; |
@@ -55,5 +55,11 @@ | @@ -55,5 +55,11 @@ | ||
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.customer}}"> | ||
62 | + </tb-relation-table> | ||
63 | + </md-tab> | ||
58 | </md-tabs> | 64 | </md-tabs> |
59 | </tb-grid> | 65 | </tb-grid> |
@@ -56,4 +56,10 @@ | @@ -56,4 +56,10 @@ | ||
56 | default-event-type="{{vm.types.eventType.alarm.value}}"> | 56 | default-event-type="{{vm.types.eventType.alarm.value}}"> |
57 | </tb-event-table> | 57 | </tb-event-table> |
58 | </md-tab> | 58 | </md-tab> |
59 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | ||
60 | + <tb-relation-table flex | ||
61 | + entity-id="vm.grid.operatingItem().id.id" | ||
62 | + entity-type="{{vm.types.entityType.device}}"> | ||
63 | + </tb-relation-table> | ||
64 | + </md-tab> | ||
59 | </tb-grid> | 65 | </tb-grid> |
@@ -41,7 +41,7 @@ | @@ -41,7 +41,7 @@ | ||
41 | </md-autocomplete> | 41 | </md-autocomplete> |
42 | <md-chip-template> | 42 | <md-chip-template> |
43 | <span> | 43 | <span> |
44 | - <strong>{{itemName($chip)}}</strong> | 44 | + <strong>{{$chip.name}}</strong> |
45 | </span> | 45 | </span> |
46 | </md-chip-template> | 46 | </md-chip-template> |
47 | </md-chips> | 47 | </md-chips> |
@@ -17,6 +17,9 @@ | @@ -17,6 +17,9 @@ | ||
17 | --> | 17 | --> |
18 | <div layout='row' class="tb-entity-select"> | 18 | <div layout='row' class="tb-entity-select"> |
19 | <tb-entity-type-select style="min-width: 100px;" | 19 | <tb-entity-type-select style="min-width: 100px;" |
20 | + the-form="theForm" | ||
21 | + ng-disabled="disabled" | ||
22 | + tb-required="tbRequired" | ||
20 | ng-model="model.entityType"> | 23 | ng-model="model.entityType"> |
21 | </tb-entity-type-select> | 24 | </tb-entity-type-select> |
22 | <tb-entity-autocomplete flex ng-if="model.entityType" | 25 | <tb-entity-autocomplete flex ng-if="model.entityType" |
@@ -29,6 +29,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe | @@ -29,6 +29,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe | ||
29 | var template = $templateCache.get(entityTypeSelectTemplate); | 29 | var template = $templateCache.get(entityTypeSelectTemplate); |
30 | element.html(template); | 30 | element.html(template); |
31 | 31 | ||
32 | + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; | ||
33 | + | ||
32 | if (angular.isDefined(attrs.hideLabel)) { | 34 | if (angular.isDefined(attrs.hideLabel)) { |
33 | scope.showLabel = false; | 35 | scope.showLabel = false; |
34 | } else { | 36 | } else { |
@@ -103,6 +105,9 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe | @@ -103,6 +105,9 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe | ||
103 | require: "^ngModel", | 105 | require: "^ngModel", |
104 | link: linker, | 106 | link: linker, |
105 | scope: { | 107 | scope: { |
108 | + theForm: '=?', | ||
109 | + tbRequired: '=?', | ||
110 | + disabled:'=ngDisabled', | ||
106 | allowedEntityTypes: "=?" | 111 | allowedEntityTypes: "=?" |
107 | } | 112 | } |
108 | }; | 113 | }; |
@@ -17,9 +17,13 @@ | @@ -17,9 +17,13 @@ | ||
17 | --> | 17 | --> |
18 | <md-input-container> | 18 | <md-input-container> |
19 | <label ng-if="showLabel">{{ 'entity.type' | translate }}</label> | 19 | <label ng-if="showLabel">{{ 'entity.type' | translate }}</label> |
20 | - <md-select ng-model="entityType" class="tb-entity-type-select" aria-label="{{ 'entity.type' | translate }}"> | 20 | + <md-select ng-required="tbRequired" ng-disabled="disabled" name="entityType" |
21 | + ng-model="entityType" class="tb-entity-type-select" aria-label="{{ 'entity.type' | translate }}"> | ||
21 | <md-option ng-repeat="type in entityTypes" ng-value="type"> | 22 | <md-option ng-repeat="type in entityTypes" ng-value="type"> |
22 | {{typeName(type) | translate}} | 23 | {{typeName(type) | translate}} |
23 | </md-option> | 24 | </md-option> |
24 | </md-select> | 25 | </md-select> |
25 | -</md-input-container> | ||
26 | + <div ng-messages="theForm.entityType.$error"> | ||
27 | + <div ng-message="required" translate>entity.type-required</div> | ||
28 | + </div> | ||
29 | +</md-input-container> |
@@ -27,6 +27,7 @@ import AddAttributeDialogController from './attribute/add-attribute-dialog.contr | @@ -27,6 +27,7 @@ import AddAttributeDialogController from './attribute/add-attribute-dialog.contr | ||
27 | import AddWidgetToDashboardDialogController from './attribute/add-widget-to-dashboard-dialog.controller'; | 27 | import AddWidgetToDashboardDialogController from './attribute/add-widget-to-dashboard-dialog.controller'; |
28 | import AttributeTableDirective from './attribute/attribute-table.directive'; | 28 | import AttributeTableDirective from './attribute/attribute-table.directive'; |
29 | import RelationTableDirective from './relation/relation-table.directive'; | 29 | import RelationTableDirective from './relation/relation-table.directive'; |
30 | +import RelationTypeAutocompleteDirective from './relation/relation-type-autocomplete.directive'; | ||
30 | 31 | ||
31 | export default angular.module('thingsboard.entity', []) | 32 | export default angular.module('thingsboard.entity', []) |
32 | .controller('EntityAliasesController', EntityAliasesController) | 33 | .controller('EntityAliasesController', EntityAliasesController) |
@@ -42,4 +43,5 @@ export default angular.module('thingsboard.entity', []) | @@ -42,4 +43,5 @@ export default angular.module('thingsboard.entity', []) | ||
42 | .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) | 43 | .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective) |
43 | .directive('tbAttributeTable', AttributeTableDirective) | 44 | .directive('tbAttributeTable', AttributeTableDirective) |
44 | .directive('tbRelationTable', RelationTableDirective) | 45 | .directive('tbRelationTable', RelationTableDirective) |
46 | + .directive('tbRelationTypeAutocomplete', RelationTypeAutocompleteDirective) | ||
45 | .name; | 47 | .name; |
@@ -14,14 +14,20 @@ | @@ -14,14 +14,20 @@ | ||
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | /*@ngInject*/ | 16 | /*@ngInject*/ |
17 | -export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, from) { | 17 | +export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, direction, entityId) { |
18 | 18 | ||
19 | var vm = this; | 19 | var vm = this; |
20 | 20 | ||
21 | vm.types = types; | 21 | vm.types = types; |
22 | + vm.direction = direction; | ||
23 | + vm.targetEntityId = {}; | ||
22 | 24 | ||
23 | vm.relation = {}; | 25 | vm.relation = {}; |
24 | - vm.relation.from = from; | 26 | + if (vm.direction == vm.types.entitySearchDirection.from) { |
27 | + vm.relation.from = entityId; | ||
28 | + } else { | ||
29 | + vm.relation.to = entityId; | ||
30 | + } | ||
25 | vm.relation.type = types.entityRelationType.contains; | 31 | vm.relation.type = types.entityRelationType.contains; |
26 | 32 | ||
27 | vm.add = add; | 33 | vm.add = add; |
@@ -32,6 +38,11 @@ export default function AddRelationDialogController($scope, $mdDialog, types, en | @@ -32,6 +38,11 @@ export default function AddRelationDialogController($scope, $mdDialog, types, en | ||
32 | } | 38 | } |
33 | 39 | ||
34 | function add() { | 40 | function add() { |
41 | + if (vm.direction == vm.types.entitySearchDirection.from) { | ||
42 | + vm.relation.to = vm.targetEntityId; | ||
43 | + } else { | ||
44 | + vm.relation.from = vm.targetEntityId; | ||
45 | + } | ||
35 | $scope.theForm.$setPristine(); | 46 | $scope.theForm.$setPristine(); |
36 | entityRelationService.saveRelation(vm.relation).then( | 47 | entityRelationService.saveRelation(vm.relation).then( |
37 | function success() { | 48 | function success() { |
@@ -32,19 +32,16 @@ | @@ -32,19 +32,16 @@ | ||
32 | <div class="md-dialog-content"> | 32 | <div class="md-dialog-content"> |
33 | <md-content class="md-padding" layout="column"> | 33 | <md-content class="md-padding" layout="column"> |
34 | <fieldset ng-disabled="loading"> | 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> | 35 | + <tb-relation-type-autocomplete ng-model="vm.relation.type" |
36 | + tb-required="true" | ||
37 | + ng-disabled="loading"> | ||
38 | + </tb-relation-type-autocomplete> | ||
39 | + <small>{{(vm.direction == vm.types.entitySearchDirection.from ? | ||
40 | + 'relation.to-entity' : 'relation.from-entity') | translate}}</small> | ||
44 | <tb-entity-select flex | 41 | <tb-entity-select flex |
45 | the-form="theForm" | 42 | the-form="theForm" |
46 | tb-required="true" | 43 | tb-required="true" |
47 | - ng-model="vm.relation.to"> | 44 | + ng-model="vm.targetEntityId"> |
48 | </tb-entity-select> | 45 | </tb-entity-select> |
49 | </fieldset> | 46 | </fieldset> |
50 | </md-content> | 47 | </md-content> |
@@ -45,13 +45,17 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | @@ -45,13 +45,17 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | ||
45 | 45 | ||
46 | let vm = this; | 46 | let vm = this; |
47 | 47 | ||
48 | + vm.types = types; | ||
49 | + | ||
50 | + vm.direction = vm.types.entitySearchDirection.from; | ||
51 | + | ||
48 | vm.relations = []; | 52 | vm.relations = []; |
49 | vm.relationsCount = 0; | 53 | vm.relationsCount = 0; |
50 | vm.allRelations = []; | 54 | vm.allRelations = []; |
51 | vm.selectedRelations = []; | 55 | vm.selectedRelations = []; |
52 | 56 | ||
53 | vm.query = { | 57 | vm.query = { |
54 | - order: 'typeName', | 58 | + order: 'type', |
55 | limit: 5, | 59 | limit: 5, |
56 | page: 1, | 60 | page: 1, |
57 | search: null | 61 | search: null |
@@ -62,19 +66,23 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | @@ -62,19 +66,23 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | ||
62 | vm.onReorder = onReorder; | 66 | vm.onReorder = onReorder; |
63 | vm.onPaginate = onPaginate; | 67 | vm.onPaginate = onPaginate; |
64 | vm.addRelation = addRelation; | 68 | vm.addRelation = addRelation; |
65 | - vm.editRelation = editRelation; | ||
66 | vm.deleteRelation = deleteRelation; | 69 | vm.deleteRelation = deleteRelation; |
67 | vm.deleteRelations = deleteRelations; | 70 | vm.deleteRelations = deleteRelations; |
68 | vm.reloadRelations = reloadRelations; | 71 | vm.reloadRelations = reloadRelations; |
69 | vm.updateRelations = updateRelations; | 72 | vm.updateRelations = updateRelations; |
70 | 73 | ||
71 | - | ||
72 | $scope.$watch("vm.entityId", function(newVal, prevVal) { | 74 | $scope.$watch("vm.entityId", function(newVal, prevVal) { |
73 | if (newVal && !angular.equals(newVal, prevVal)) { | 75 | if (newVal && !angular.equals(newVal, prevVal)) { |
74 | reloadRelations(); | 76 | reloadRelations(); |
75 | } | 77 | } |
76 | }); | 78 | }); |
77 | 79 | ||
80 | + $scope.$watch("vm.direction", function(newVal, prevVal) { | ||
81 | + if (newVal && !angular.equals(newVal, prevVal)) { | ||
82 | + reloadRelations(); | ||
83 | + } | ||
84 | + }); | ||
85 | + | ||
78 | $scope.$watch("vm.query.search", function(newVal, prevVal) { | 86 | $scope.$watch("vm.query.search", function(newVal, prevVal) { |
79 | if (!angular.equals(newVal, prevVal) && vm.query.search != null) { | 87 | if (!angular.equals(newVal, prevVal) && vm.query.search != null) { |
80 | updateRelations(); | 88 | updateRelations(); |
@@ -102,7 +110,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | @@ -102,7 +110,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | ||
102 | if ($event) { | 110 | if ($event) { |
103 | $event.stopPropagation(); | 111 | $event.stopPropagation(); |
104 | } | 112 | } |
105 | - var from = { | 113 | + var entityId = { |
106 | id: vm.entityId, | 114 | id: vm.entityId, |
107 | entityType: vm.entityType | 115 | entityType: vm.entityType |
108 | }; | 116 | }; |
@@ -111,7 +119,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | @@ -111,7 +119,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | ||
111 | controllerAs: 'vm', | 119 | controllerAs: 'vm', |
112 | templateUrl: addRelationTemplate, | 120 | templateUrl: addRelationTemplate, |
113 | parent: angular.element($document[0].body), | 121 | parent: angular.element($document[0].body), |
114 | - locals: { from: from }, | 122 | + locals: { direction: vm.direction, entityId: entityId }, |
115 | fullscreen: true, | 123 | fullscreen: true, |
116 | targetEvent: $event | 124 | targetEvent: $event |
117 | }).then(function () { | 125 | }).then(function () { |
@@ -120,36 +128,100 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | @@ -120,36 +128,100 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | ||
120 | }); | 128 | }); |
121 | } | 129 | } |
122 | 130 | ||
123 | - function editRelation($event, /*relation*/) { | 131 | + function deleteRelation($event, relation) { |
124 | if ($event) { | 132 | if ($event) { |
125 | $event.stopPropagation(); | 133 | $event.stopPropagation(); |
126 | } | 134 | } |
127 | - //TODO: | ||
128 | - } | 135 | + if (relation) { |
136 | + var title; | ||
137 | + var content; | ||
138 | + if (vm.direction == vm.types.entitySearchDirection.from) { | ||
139 | + title = $translate.instant('relation.delete-to-relation-title', {entityName: relation.toName}); | ||
140 | + content = $translate.instant('relation.delete-to-relation-text', {entityName: relation.toName}); | ||
141 | + } else { | ||
142 | + title = $translate.instant('relation.delete-from-relation-title', {entityName: relation.fromName}); | ||
143 | + content = $translate.instant('relation.delete-from-relation-text', {entityName: relation.fromName}); | ||
144 | + } | ||
129 | 145 | ||
130 | - function deleteRelation($event, /*relation*/) { | ||
131 | - if ($event) { | ||
132 | - $event.stopPropagation(); | 146 | + var confirm = $mdDialog.confirm() |
147 | + .targetEvent($event) | ||
148 | + .title(title) | ||
149 | + .htmlContent(content) | ||
150 | + .ariaLabel(title) | ||
151 | + .cancel($translate.instant('action.no')) | ||
152 | + .ok($translate.instant('action.yes')); | ||
153 | + $mdDialog.show(confirm).then(function () { | ||
154 | + entityRelationService.deleteRelation( | ||
155 | + relation.from.id, | ||
156 | + relation.from.entityType, | ||
157 | + relation.type, | ||
158 | + relation.to.id, | ||
159 | + relation.to.entityType).then( | ||
160 | + function success() { | ||
161 | + reloadRelations(); | ||
162 | + } | ||
163 | + ); | ||
164 | + }); | ||
133 | } | 165 | } |
134 | - //TODO: | ||
135 | } | 166 | } |
136 | 167 | ||
137 | function deleteRelations($event) { | 168 | function deleteRelations($event) { |
138 | if ($event) { | 169 | if ($event) { |
139 | $event.stopPropagation(); | 170 | $event.stopPropagation(); |
140 | } | 171 | } |
141 | - //TODO: | 172 | + if (vm.selectedRelations && vm.selectedRelations.length > 0) { |
173 | + var title; | ||
174 | + var content; | ||
175 | + if (vm.direction == vm.types.entitySearchDirection.from) { | ||
176 | + title = $translate.instant('relation.delete-to-relations-title', {count: vm.selectedRelations.length}, 'messageformat'); | ||
177 | + content = $translate.instant('relation.delete-to-relations-text'); | ||
178 | + } else { | ||
179 | + title = $translate.instant('relation.delete-from-relations-title', {count: vm.selectedRelations.length}, 'messageformat'); | ||
180 | + content = $translate.instant('relation.delete-from-relations-text'); | ||
181 | + } | ||
182 | + var confirm = $mdDialog.confirm() | ||
183 | + .targetEvent($event) | ||
184 | + .title(title) | ||
185 | + .htmlContent(content) | ||
186 | + .ariaLabel(title) | ||
187 | + .cancel($translate.instant('action.no')) | ||
188 | + .ok($translate.instant('action.yes')); | ||
189 | + $mdDialog.show(confirm).then(function () { | ||
190 | + var tasks = []; | ||
191 | + for (var i=0;i<vm.selectedRelations.length;i++) { | ||
192 | + var relation = vm.selectedRelations[i]; | ||
193 | + tasks.push( entityRelationService.deleteRelation( | ||
194 | + relation.from.id, | ||
195 | + relation.from.entityType, | ||
196 | + relation.type, | ||
197 | + relation.to.id, | ||
198 | + relation.to.entityType)); | ||
199 | + } | ||
200 | + $q.all(tasks).then(function () { | ||
201 | + reloadRelations(); | ||
202 | + }); | ||
203 | + | ||
204 | + }); | ||
205 | + } | ||
142 | } | 206 | } |
143 | 207 | ||
144 | function reloadRelations () { | 208 | function reloadRelations () { |
145 | vm.allRelations.length = 0; | 209 | vm.allRelations.length = 0; |
146 | vm.relations.length = 0; | 210 | vm.relations.length = 0; |
147 | - vm.relationsPromise = entityRelationService.findInfoByFrom(vm.entityId, vm.entityType); | 211 | + vm.relationsPromise; |
212 | + if (vm.direction == vm.types.entitySearchDirection.from) { | ||
213 | + vm.relationsPromise = entityRelationService.findInfoByFrom(vm.entityId, vm.entityType); | ||
214 | + } else { | ||
215 | + vm.relationsPromise = entityRelationService.findInfoByTo(vm.entityId, vm.entityType); | ||
216 | + } | ||
148 | vm.relationsPromise.then( | 217 | vm.relationsPromise.then( |
149 | function success(allRelations) { | 218 | function success(allRelations) { |
150 | allRelations.forEach(function(relation) { | 219 | allRelations.forEach(function(relation) { |
151 | - relation.typeName = $translate.instant('relation.relation-type.' + relation.type); | ||
152 | - relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType)); | 220 | + if (vm.direction == vm.types.entitySearchDirection.from) { |
221 | + relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType)); | ||
222 | + } else { | ||
223 | + relation.fromEntityTypeName = $translate.instant(utils.entityTypeName(relation.from.entityType)); | ||
224 | + } | ||
153 | }); | 225 | }); |
154 | vm.allRelations = allRelations; | 226 | vm.allRelations = allRelations; |
155 | vm.selectedRelations = []; | 227 | vm.selectedRelations = []; |
@@ -16,11 +16,22 @@ | @@ -16,11 +16,22 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <md-content flex class="md-padding tb-absolute-fill tb-relation-table tb-data-table" layout="column"> | 18 | <md-content flex class="md-padding tb-absolute-fill tb-relation-table tb-data-table" layout="column"> |
19 | + <section layout="row"> | ||
20 | + <md-input-container class="md-block" style="width: 200px;"> | ||
21 | + <label translate>relation.direction</label> | ||
22 | + <md-select ng-model="vm.direction" ng-disabled="loading"> | ||
23 | + <md-option ng-repeat="direction in vm.types.entitySearchDirection" ng-value="direction"> | ||
24 | + {{ ('relation.search-direction.' + direction) | translate}} | ||
25 | + </md-option> | ||
26 | + </md-select> | ||
27 | + </md-input-container> | ||
28 | + </section> | ||
19 | <div layout="column" class="md-whiteframe-z1"> | 29 | <div layout="column" class="md-whiteframe-z1"> |
20 | <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length | 30 | <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length |
21 | && vm.query.search === null"> | 31 | && vm.query.search === null"> |
22 | <div class="md-toolbar-tools"> | 32 | <div class="md-toolbar-tools"> |
23 | - <span translate>relation.entity-relations</span> | 33 | + <span>{{(vm.direction == vm.types.entitySearchDirection.from ? |
34 | + 'relation.from-relations' : 'relation.to-relations') | translate}}</span> | ||
24 | <span flex></span> | 35 | <span flex></span> |
25 | <md-button class="md-icon-button" ng-click="vm.addRelation($event)"> | 36 | <md-button class="md-icon-button" ng-click="vm.addRelation($event)"> |
26 | <md-icon>add</md-icon> | 37 | <md-icon>add</md-icon> |
@@ -66,7 +77,7 @@ | @@ -66,7 +77,7 @@ | ||
66 | <md-toolbar class="md-table-toolbar alternate" ng-show="vm.selectedRelations.length"> | 77 | <md-toolbar class="md-table-toolbar alternate" ng-show="vm.selectedRelations.length"> |
67 | <div class="md-toolbar-tools"> | 78 | <div class="md-toolbar-tools"> |
68 | <span translate | 79 | <span translate |
69 | - translate-values="{count: selectedRelations.length}" | 80 | + translate-values="{count: vm.selectedRelations.length}" |
70 | translate-interpolation="messageformat">relation.selected-relations</span> | 81 | translate-interpolation="messageformat">relation.selected-relations</span> |
71 | <span flex></span> | 82 | <span flex></span> |
72 | <md-button class="md-icon-button" ng-click="vm.deleteRelations($event)"> | 83 | <md-button class="md-icon-button" ng-click="vm.deleteRelations($event)"> |
@@ -81,25 +92,26 @@ | @@ -81,25 +92,26 @@ | ||
81 | <table md-table md-row-select multiple="" ng-model="vm.selectedRelations" md-progress="vm.relationsDeferred.promise"> | 92 | <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"> | 93 | <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder"> |
83 | <tr md-row> | 94 | <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> | 95 | + <th md-column md-order-by="type"><span translate>relation.type</span></th> |
96 | + <th md-column ng-if="vm.direction == vm.types.entitySearchDirection.from" | ||
97 | + md-order-by="toEntityTypeName"><span translate>relation.to-entity-type</span></th> | ||
98 | + <th md-column ng-if="vm.direction == vm.types.entitySearchDirection.to" | ||
99 | + md-order-by="fromEntityTypeName"><span translate>relation.from-entity-type</span></th> | ||
100 | + <th md-column ng-if="vm.direction == vm.types.entitySearchDirection.from" | ||
101 | + md-order-by="toName"><span translate>relation.to-entity-name</span></th> | ||
102 | + <th md-column ng-if="vm.direction == vm.types.entitySearchDirection.to" | ||
103 | + md-order-by="fromName"><span translate>relation.from-entity-name</span></th> | ||
87 | <th md-column><span> </span></th> | 104 | <th md-column><span> </span></th> |
88 | </tr> | 105 | </tr> |
89 | </thead> | 106 | </thead> |
90 | <tbody md-body> | 107 | <tbody md-body> |
91 | <tr md-row md-select="relation" md-select-id="relation" md-auto-select ng-repeat="relation in vm.relations"> | 108 | <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> | 109 | + <td md-cell>{{ relation.type }}</td> |
110 | + <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.from">{{ relation.toEntityTypeName }}</td> | ||
111 | + <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.to">{{ relation.fromEntityTypeName }}</td> | ||
112 | + <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.from">{{ relation.toName }}</td> | ||
113 | + <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.to">{{ relation.fromName }}</td> | ||
95 | <td md-cell class="tb-action-cell"> | 114 | <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)"> | 115 | <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> | 116 | <md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">delete</md-icon> |
105 | <md-tooltip md-direction="top"> | 117 | <md-tooltip md-direction="top"> |
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 './relation-type-autocomplete.scss'; | ||
17 | + | ||
18 | +/* eslint-disable import/no-unresolved, import/default */ | ||
19 | + | ||
20 | +import relationTypeAutocompleteTemplate from './relation-type-autocomplete.tpl.html'; | ||
21 | + | ||
22 | +/* eslint-enable import/no-unresolved, import/default */ | ||
23 | + | ||
24 | +/*@ngInject*/ | ||
25 | +export default function RelationTypeAutocomplete($compile, $templateCache, $q, $filter, assetService, deviceService, types) { | ||
26 | + | ||
27 | + var linker = function (scope, element, attrs, ngModelCtrl) { | ||
28 | + var template = $templateCache.get(relationTypeAutocompleteTemplate); | ||
29 | + element.html(template); | ||
30 | + | ||
31 | + scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; | ||
32 | + scope.relationType = null; | ||
33 | + scope.relationTypeSearchText = ''; | ||
34 | + scope.relationTypes = []; | ||
35 | + for (var type in types.entityRelationType) { | ||
36 | + scope.relationTypes.push(types.entityRelationType[type]); | ||
37 | + } | ||
38 | + | ||
39 | + scope.fetchRelationTypes = function(searchText) { | ||
40 | + var deferred = $q.defer(); | ||
41 | + var result = $filter('filter')(scope.relationTypes, {'$': searchText}); | ||
42 | + if (result && result.length) { | ||
43 | + deferred.resolve(result); | ||
44 | + } else { | ||
45 | + deferred.resolve([searchText]); | ||
46 | + } | ||
47 | + return deferred.promise; | ||
48 | + } | ||
49 | + | ||
50 | + scope.relationTypeSearchTextChanged = function() { | ||
51 | + } | ||
52 | + | ||
53 | + scope.updateView = function () { | ||
54 | + if (!scope.disabled) { | ||
55 | + ngModelCtrl.$setViewValue(scope.relationType); | ||
56 | + } | ||
57 | + } | ||
58 | + | ||
59 | + ngModelCtrl.$render = function () { | ||
60 | + scope.relationType = ngModelCtrl.$viewValue; | ||
61 | + } | ||
62 | + | ||
63 | + scope.$watch('relationType', function (newValue, prevValue) { | ||
64 | + if (!angular.equals(newValue, prevValue)) { | ||
65 | + scope.updateView(); | ||
66 | + } | ||
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 | +.tb-relation-type-autocomplete { | ||
17 | + .tb-relation-type-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="relationType" | ||
22 | + ng-model="relationType" | ||
23 | + md-selected-item="relationType" | ||
24 | + md-search-text="relationTypeSearchText" | ||
25 | + md-search-text-change="relationTypeSearchTextChanged()" | ||
26 | + md-items="item in fetchRelationTypes(relationTypeSearchText)" | ||
27 | + md-item-text="item" | ||
28 | + md-min-length="0" | ||
29 | + md-floating-label="{{ 'relation.relation-type' | translate }}" | ||
30 | + md-select-on-match="true" | ||
31 | + md-menu-class="tb-relation-type-autocomplete"> | ||
32 | + <md-item-template> | ||
33 | + <div class="tb-relation-type-item"> | ||
34 | + <span md-highlight-text="relationTypeSearchText" md-highlight-flags="^i">{{item}}</span> | ||
35 | + </div> | ||
36 | + </md-item-template> | ||
37 | + <div ng-messages="theForm.relationType.$error"> | ||
38 | + <div translate ng-message="required">relation.relation-type-required</div> | ||
39 | + </div> | ||
40 | +</md-autocomplete> |
@@ -544,6 +544,7 @@ export default angular.module('thingsboard.locale', []) | @@ -544,6 +544,7 @@ export default angular.module('thingsboard.locale', []) | ||
544 | "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", | 544 | "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", |
545 | "all-subtypes": "All", | 545 | "all-subtypes": "All", |
546 | "type": "Type", | 546 | "type": "Type", |
547 | + "type-required": "Entity type is required.", | ||
547 | "type-device": "Device", | 548 | "type-device": "Device", |
548 | "type-asset": "Asset", | 549 | "type-asset": "Asset", |
549 | "type-rule": "Rule", | 550 | "type-rule": "Rule", |
@@ -718,19 +719,33 @@ export default angular.module('thingsboard.locale', []) | @@ -718,19 +719,33 @@ export default angular.module('thingsboard.locale', []) | ||
718 | }, | 719 | }, |
719 | "relation": { | 720 | "relation": { |
720 | "relations": "Relations", | 721 | "relations": "Relations", |
721 | - "entity-relations": "Entity relations", | 722 | + "direction": "Direction", |
723 | + "search-direction": { | ||
724 | + "FROM": "From", | ||
725 | + "TO": "To" | ||
726 | + }, | ||
727 | + "from-relations": "Outbound relations", | ||
728 | + "to-relations": "Inbound relations", | ||
722 | "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected", | 729 | "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected", |
723 | "type": "Type", | 730 | "type": "Type", |
724 | - "to-entity-type": "Entity type", | ||
725 | - "to-entity-name": "Entity name", | ||
726 | - "edit": "Edit relation", | 731 | + "to-entity-type": "To entity type", |
732 | + "to-entity-name": "To entity name", | ||
733 | + "from-entity-type": "From entity type", | ||
734 | + "from-entity-name": "From entity name", | ||
735 | + "to-entity": "To entity", | ||
736 | + "from-entity": "From entity", | ||
727 | "delete": "Delete relation", | 737 | "delete": "Delete relation", |
728 | "relation-type": "Relation type", | 738 | "relation-type": "Relation type", |
729 | - "relation-types": { | ||
730 | - "Contains": "Contains", | ||
731 | - "Manages": "Manages" | ||
732 | - }, | ||
733 | - "add": "Add relation" | 739 | + "relation-type-required": "Relation type is required.", |
740 | + "add": "Add relation", | ||
741 | + "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?", | ||
742 | + "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.", | ||
743 | + "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?", | ||
744 | + "delete-to-relations-text": "Be careful, after the confirmation all selected relations will be removed and corresponding entities will be unrelated from the current entity.", | ||
745 | + "delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?", | ||
746 | + "delete-from-relation-text": "Be careful, after the confirmation current entity will be unrelated from the entity '{{entityName}}'.", | ||
747 | + "delete-from-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?", | ||
748 | + "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities." | ||
734 | }, | 749 | }, |
735 | "rule": { | 750 | "rule": { |
736 | "rule": "Rule", | 751 | "rule": "Rule", |
@@ -56,5 +56,11 @@ | @@ -56,5 +56,11 @@ | ||
56 | disabled-event-types="{{vm.types.eventType.alarm.value}}"> | 56 | disabled-event-types="{{vm.types.eventType.alarm.value}}"> |
57 | </tb-event-table> | 57 | </tb-event-table> |
58 | </md-tab> | 58 | </md-tab> |
59 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | ||
60 | + <tb-relation-table flex | ||
61 | + entity-id="vm.grid.operatingItem().id.id" | ||
62 | + entity-type="{{vm.types.entityType.plugin}}"> | ||
63 | + </tb-relation-table> | ||
64 | + </md-tab> | ||
59 | </md-tabs> | 65 | </md-tabs> |
60 | </tb-grid> | 66 | </tb-grid> |
@@ -56,5 +56,11 @@ | @@ -56,5 +56,11 @@ | ||
56 | disabled-event-types="{{vm.types.eventType.alarm.value}}"> | 56 | disabled-event-types="{{vm.types.eventType.alarm.value}}"> |
57 | </tb-event-table> | 57 | </tb-event-table> |
58 | </md-tab> | 58 | </md-tab> |
59 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | ||
60 | + <tb-relation-table flex | ||
61 | + entity-id="vm.grid.operatingItem().id.id" | ||
62 | + entity-type="{{vm.types.entityType.rule}}"> | ||
63 | + </tb-relation-table> | ||
64 | + </md-tab> | ||
59 | </md-tabs> | 65 | </md-tabs> |
60 | </tb-grid> | 66 | </tb-grid> |
@@ -53,5 +53,11 @@ | @@ -53,5 +53,11 @@ | ||
53 | default-event-type="{{vm.types.eventType.alarm.value}}"> | 53 | default-event-type="{{vm.types.eventType.alarm.value}}"> |
54 | </tb-event-table> | 54 | </tb-event-table> |
55 | </md-tab> | 55 | </md-tab> |
56 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | ||
57 | + <tb-relation-table flex | ||
58 | + entity-id="vm.grid.operatingItem().id.id" | ||
59 | + entity-type="{{vm.types.entityType.tenant}}"> | ||
60 | + </tb-relation-table> | ||
61 | + </md-tab> | ||
56 | </md-tabs> | 62 | </md-tabs> |
57 | </tb-grid> | 63 | </tb-grid> |
@@ -436,6 +436,7 @@ md-tabs.tb-headless { | @@ -436,6 +436,7 @@ md-tabs.tb-headless { | ||
436 | ***********************/ | 436 | ***********************/ |
437 | 437 | ||
438 | section.tb-header-buttons { | 438 | section.tb-header-buttons { |
439 | + pointer-events: none; | ||
439 | position: absolute; | 440 | position: absolute; |
440 | right: 0px; | 441 | right: 0px; |
441 | top: 86px; | 442 | top: 86px; |