Showing
29 changed files
with
365 additions
and
85 deletions
... | ... | @@ -55,6 +55,7 @@ import org.thingsboard.server.common.data.page.TimePageLink; |
55 | 55 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
56 | 56 | import org.thingsboard.server.common.data.rule.RuleChain; |
57 | 57 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
58 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
58 | 59 | import org.thingsboard.server.common.data.rule.RuleNode; |
59 | 60 | import org.thingsboard.server.common.msg.TbMsg; |
60 | 61 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
... | ... | @@ -231,13 +232,19 @@ public class RuleChainController extends BaseController { |
231 | 232 | @ResponseBody |
232 | 233 | public TextPageData<RuleChain> getRuleChains( |
233 | 234 | @RequestParam int limit, |
235 | + @RequestParam(value = "type", required = false) String typeStr, | |
234 | 236 | @RequestParam(required = false) String textSearch, |
235 | 237 | @RequestParam(required = false) String idOffset, |
236 | 238 | @RequestParam(required = false) String textOffset) throws ThingsboardException { |
237 | 239 | try { |
238 | 240 | TenantId tenantId = getCurrentUser().getTenantId(); |
239 | 241 | TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); |
240 | - return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink)); | |
242 | + if (typeStr != null && typeStr.trim().length() > 0) { | |
243 | + RuleChainType type = RuleChainType.valueOf(typeStr); | |
244 | + return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink)); | |
245 | + } else { | |
246 | + return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink)); | |
247 | + } | |
241 | 248 | } catch (Exception e) { |
242 | 249 | throw handleException(e); |
243 | 250 | } | ... | ... |
... | ... | @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.alarm.Alarm; |
37 | 37 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
38 | 38 | import org.thingsboard.server.common.data.alarm.AlarmStatus; |
39 | 39 | import org.thingsboard.server.common.data.asset.Asset; |
40 | +import org.thingsboard.server.common.data.audit.ActionType; | |
40 | 41 | import org.thingsboard.server.common.data.edge.Edge; |
41 | 42 | import org.thingsboard.server.common.data.edge.EdgeQueueEntry; |
42 | 43 | import org.thingsboard.server.common.data.id.AssetId; |
... | ... | @@ -44,10 +45,12 @@ import org.thingsboard.server.common.data.id.CustomerId; |
44 | 45 | import org.thingsboard.server.common.data.id.DeviceId; |
45 | 46 | import org.thingsboard.server.common.data.id.EdgeId; |
46 | 47 | import org.thingsboard.server.common.data.id.EntityId; |
48 | +import org.thingsboard.server.common.data.id.EntityIdFactory; | |
47 | 49 | import org.thingsboard.server.common.data.id.EntityViewId; |
48 | 50 | import org.thingsboard.server.common.data.id.TenantId; |
49 | 51 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
50 | 52 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
53 | +import org.thingsboard.server.common.data.kv.DataType; | |
51 | 54 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
52 | 55 | import org.thingsboard.server.common.data.page.TimePageData; |
53 | 56 | import org.thingsboard.server.common.data.page.TimePageLink; |
... | ... | @@ -59,6 +62,7 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; |
59 | 62 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
60 | 63 | import org.thingsboard.server.common.data.rule.RuleNode; |
61 | 64 | import org.thingsboard.server.common.msg.TbMsg; |
65 | +import org.thingsboard.server.common.msg.TbMsgDataType; | |
62 | 66 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
63 | 67 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; |
64 | 68 | import org.thingsboard.server.common.msg.session.SessionMsgType; |
... | ... | @@ -91,6 +95,7 @@ import org.thingsboard.server.gen.edge.UplinkResponseMsg; |
91 | 95 | import org.thingsboard.server.gen.edge.UserUpdateMsg; |
92 | 96 | import org.thingsboard.server.service.edge.EdgeContextComponent; |
93 | 97 | |
98 | +import javax.swing.text.html.parser.Entity; | |
94 | 99 | import java.io.IOException; |
95 | 100 | import java.util.ArrayList; |
96 | 101 | import java.util.Collections; |
... | ... | @@ -102,6 +107,9 @@ import java.util.concurrent.locks.ReentrantLock; |
102 | 107 | import java.util.function.BiConsumer; |
103 | 108 | import java.util.function.Consumer; |
104 | 109 | |
110 | +import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; | |
111 | +import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; | |
112 | + | |
105 | 113 | @Slf4j |
106 | 114 | @Data |
107 | 115 | public final class EdgeGrpcSession implements Cloneable { |
... | ... | @@ -175,7 +183,9 @@ public final class EdgeGrpcSession implements Cloneable { |
175 | 183 | do { |
176 | 184 | pageData = ctx.getEdgeService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink); |
177 | 185 | if (!pageData.getData().isEmpty()) { |
186 | + log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size()); | |
178 | 187 | for (Event event : pageData.getData()) { |
188 | + log.trace("[{}] Processing event [{}]", this.sessionId, event); | |
179 | 189 | EdgeQueueEntry entry; |
180 | 190 | try { |
181 | 191 | entry = objectMapper.treeToValue(event.getBody(), EdgeQueueEntry.class); |
... | ... | @@ -193,6 +203,9 @@ public final class EdgeGrpcSession implements Cloneable { |
193 | 203 | processCustomDownlinkMessage(entry); |
194 | 204 | break; |
195 | 205 | } |
206 | + if (ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { | |
207 | + pushEntityAttributesToEdge(entry); | |
208 | + } | |
196 | 209 | } catch (Exception e) { |
197 | 210 | log.error("Exception during processing records from queue", e); |
198 | 211 | } |
... | ... | @@ -215,6 +228,63 @@ public final class EdgeGrpcSession implements Cloneable { |
215 | 228 | } |
216 | 229 | } |
217 | 230 | |
231 | + private void pushEntityAttributesToEdge(EdgeQueueEntry entry) throws IOException { | |
232 | + EntityId entityId = null; | |
233 | + String entityName = null; | |
234 | + switch (entry.getEntityType()) { | |
235 | + case EDGE: | |
236 | + entityId = objectMapper.readValue(entry.getData(), Edge.class).getId(); | |
237 | + break; | |
238 | + case DEVICE: | |
239 | + entityId = objectMapper.readValue(entry.getData(), Device.class).getId(); | |
240 | + break; | |
241 | + case ASSET: | |
242 | + entityId = objectMapper.readValue(entry.getData(), Asset.class).getId(); | |
243 | + break; | |
244 | + case ENTITY_VIEW: | |
245 | + entityId = objectMapper.readValue(entry.getData(), EntityView.class).getId(); | |
246 | + break; | |
247 | + case DASHBOARD: | |
248 | + entityId = objectMapper.readValue(entry.getData(), Dashboard.class).getId(); | |
249 | + break; | |
250 | + } | |
251 | + if (entityId != null) { | |
252 | + ListenableFuture<List<AttributeKvEntry>> ssAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SERVER_SCOPE); | |
253 | + Futures.transform(ssAttrFuture, ssAttributes -> { | |
254 | + if (ssAttributes != null && !ssAttributes.isEmpty()) { | |
255 | + try { | |
256 | + TbMsgMetaData metaData = new TbMsgMetaData(); | |
257 | + ObjectNode entityNode = objectMapper.createObjectNode(); | |
258 | + metaData.putValue("scope", DataConstants.SERVER_SCOPE); | |
259 | + for (AttributeKvEntry attr : ssAttributes) { | |
260 | + if (attr.getDataType() == DataType.BOOLEAN) { | |
261 | + entityNode.put(attr.getKey(), attr.getBooleanValue().get()); | |
262 | + } else if (attr.getDataType() == DataType.DOUBLE) { | |
263 | + entityNode.put(attr.getKey(), attr.getDoubleValue().get()); | |
264 | + } else if (attr.getDataType() == DataType.LONG) { | |
265 | + entityNode.put(attr.getKey(), attr.getLongValue().get()); | |
266 | + } else { | |
267 | + entityNode.put(attr.getKey(), attr.getValueAsString()); | |
268 | + } | |
269 | + } | |
270 | + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, TbMsgDataType.JSON | |
271 | + , objectMapper.writeValueAsString(entityNode) | |
272 | + , null, null, 0L); | |
273 | + log.debug("Sending donwlink entity data msg, entityName [{}], tbMsg [{}]", entityName, tbMsg); | |
274 | + outputStream.onNext(ResponseMsg.newBuilder() | |
275 | + .setDownlinkMsg(constructDownlinkEntityDataMsg(entityName, tbMsg)) | |
276 | + .build()); | |
277 | + } catch (Exception e) { | |
278 | + log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e); | |
279 | + } | |
280 | + } | |
281 | + return null; | |
282 | + }); | |
283 | + ListenableFuture<List<AttributeKvEntry>> shAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SHARED_SCOPE); | |
284 | + ListenableFuture<List<AttributeKvEntry>> clAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.CLIENT_SCOPE); | |
285 | + } | |
286 | + } | |
287 | + | |
218 | 288 | private void processCustomDownlinkMessage(EdgeQueueEntry entry) throws IOException { |
219 | 289 | log.trace("Executing processCustomDownlinkMessage, entry [{}]", entry); |
220 | 290 | TbMsg tbMsg = objectMapper.readValue(entry.getData(), TbMsg.class); |
... | ... | @@ -411,7 +481,7 @@ public final class EdgeGrpcSession implements Cloneable { |
411 | 481 | return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; |
412 | 482 | case DataConstants.ENTITY_CREATED: |
413 | 483 | case DataConstants.ENTITY_ASSIGNED_TO_EDGE: |
414 | - return UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; | |
484 | + return ENTITY_CREATED_RPC_MESSAGE; | |
415 | 485 | case DataConstants.ENTITY_DELETED: |
416 | 486 | case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: |
417 | 487 | return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; |
... | ... | @@ -521,6 +591,8 @@ public final class EdgeGrpcSession implements Cloneable { |
521 | 591 | |
522 | 592 | private RuleNodeProto constructNode(RuleNode node) throws JsonProcessingException { |
523 | 593 | return RuleNodeProto.newBuilder() |
594 | + .setIdMSB(node.getId().getId().getMostSignificantBits()) | |
595 | + .setIdLSB(node.getId().getId().getLeastSignificantBits()) | |
524 | 596 | .setType(node.getType()) |
525 | 597 | .setName(node.getName()) |
526 | 598 | .setDebugMode(node.isDebugMode()) | ... | ... |
... | ... | @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.page.TimePageLink; |
27 | 27 | import org.thingsboard.server.common.data.relation.EntityRelation; |
28 | 28 | import org.thingsboard.server.common.data.rule.RuleChain; |
29 | 29 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
30 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
30 | 31 | import org.thingsboard.server.common.data.rule.RuleNode; |
31 | 32 | |
32 | 33 | import java.util.List; |
... | ... | @@ -62,6 +63,8 @@ public interface RuleChainService { |
62 | 63 | |
63 | 64 | TextPageData<RuleChain> findTenantRuleChains(TenantId tenantId, TextPageLink pageLink); |
64 | 65 | |
66 | + TextPageData<RuleChain> findTenantRuleChainsByType(TenantId tenantId, RuleChainType type, TextPageLink pageLink); | |
67 | + | |
65 | 68 | void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId); |
66 | 69 | |
67 | 70 | void deleteRuleChainsByTenantId(TenantId tenantId); | ... | ... |
... | ... | @@ -125,11 +125,13 @@ message RuleChainMetadataUpdateMsg { |
125 | 125 | } |
126 | 126 | |
127 | 127 | message RuleNodeProto { |
128 | - string type = 1; | |
129 | - string name = 2; | |
130 | - bool debugMode = 3; | |
131 | - string configuration = 4; | |
132 | - string additionalInfo = 5; | |
128 | + int64 idMSB = 1; | |
129 | + int64 idLSB = 2; | |
130 | + string type = 3; | |
131 | + string name = 4; | |
132 | + bool debugMode = 5; | |
133 | + string configuration = 6; | |
134 | + string additionalInfo = 7; | |
133 | 135 | } |
134 | 136 | |
135 | 137 | message NodeConnectionInfoProto { | ... | ... |
... | ... | @@ -113,16 +113,16 @@ public class BaseComponentDescriptorService implements ComponentDescriptorServic |
113 | 113 | @Override |
114 | 114 | protected void validateDataImpl(TenantId tenantId, ComponentDescriptor plugin) { |
115 | 115 | if (plugin.getType() == null) { |
116 | - throw new DataValidationException("Component type should be specified!."); | |
116 | + throw new DataValidationException("Component type should be specified!"); | |
117 | 117 | } |
118 | 118 | if (plugin.getScope() == null) { |
119 | - throw new DataValidationException("Component scope should be specified!."); | |
119 | + throw new DataValidationException("Component scope should be specified!"); | |
120 | 120 | } |
121 | 121 | if (StringUtils.isEmpty(plugin.getName())) { |
122 | - throw new DataValidationException("Component name should be specified!."); | |
122 | + throw new DataValidationException("Component name should be specified!"); | |
123 | 123 | } |
124 | 124 | if (StringUtils.isEmpty(plugin.getClazz())) { |
125 | - throw new DataValidationException("Component clazz should be specified!."); | |
125 | + throw new DataValidationException("Component clazz should be specified!"); | |
126 | 126 | } |
127 | 127 | } |
128 | 128 | }; | ... | ... |
... | ... | @@ -54,7 +54,7 @@ public class BaseEventService implements EventService { |
54 | 54 | public Optional<Event> saveIfNotExists(Event event) { |
55 | 55 | eventValidator.validate(event, Event::getTenantId); |
56 | 56 | if (StringUtils.isEmpty(event.getUid())) { |
57 | - throw new DataValidationException("Event uid should be specified!."); | |
57 | + throw new DataValidationException("Event uid should be specified!"); | |
58 | 58 | } |
59 | 59 | return eventDao.saveIfNotExists(event); |
60 | 60 | } |
... | ... | @@ -62,16 +62,16 @@ public class BaseEventService implements EventService { |
62 | 62 | @Override |
63 | 63 | public Optional<Event> findEvent(TenantId tenantId, EntityId entityId, String eventType, String eventUid) { |
64 | 64 | if (tenantId == null) { |
65 | - throw new DataValidationException("Tenant id should be specified!."); | |
65 | + throw new DataValidationException("Tenant id should be specified!"); | |
66 | 66 | } |
67 | 67 | if (entityId == null) { |
68 | - throw new DataValidationException("Entity id should be specified!."); | |
68 | + throw new DataValidationException("Entity id should be specified!"); | |
69 | 69 | } |
70 | 70 | if (StringUtils.isEmpty(eventType)) { |
71 | - throw new DataValidationException("Event type should be specified!."); | |
71 | + throw new DataValidationException("Event type should be specified!"); | |
72 | 72 | } |
73 | 73 | if (StringUtils.isEmpty(eventUid)) { |
74 | - throw new DataValidationException("Event uid should be specified!."); | |
74 | + throw new DataValidationException("Event uid should be specified!"); | |
75 | 75 | } |
76 | 76 | Event event = eventDao.findEvent(tenantId.getId(), entityId, eventType, eventUid); |
77 | 77 | return event != null ? Optional.of(event) : Optional.empty(); |
... | ... | @@ -99,13 +99,13 @@ public class BaseEventService implements EventService { |
99 | 99 | @Override |
100 | 100 | protected void validateDataImpl(TenantId tenantId, Event event) { |
101 | 101 | if (event.getEntityId() == null) { |
102 | - throw new DataValidationException("Entity id should be specified!."); | |
102 | + throw new DataValidationException("Entity id should be specified!"); | |
103 | 103 | } |
104 | 104 | if (StringUtils.isEmpty(event.getType())) { |
105 | - throw new DataValidationException("Event type should be specified!."); | |
105 | + throw new DataValidationException("Event type should be specified!"); | |
106 | 106 | } |
107 | 107 | if (event.getBody() == null) { |
108 | - throw new DataValidationException("Event body should be specified!."); | |
108 | + throw new DataValidationException("Event body should be specified!"); | |
109 | 109 | } |
110 | 110 | } |
111 | 111 | }; | ... | ... |
... | ... | @@ -343,6 +343,7 @@ public class ModelConstants { |
343 | 343 | public static final String RULE_CHAIN_ASSIGNED_EDGES_PROPERTY = "assigned_edges"; |
344 | 344 | |
345 | 345 | public static final String RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_and_search_text"; |
346 | + public static final String RULE_CHAIN_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_by_type_and_search_text"; | |
346 | 347 | |
347 | 348 | /** |
348 | 349 | * Cassandra rule node constants. | ... | ... |
... | ... | @@ -106,11 +106,7 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> { |
106 | 106 | } |
107 | 107 | this.tenantId = DaoUtil.getId(ruleChain.getTenantId()); |
108 | 108 | this.name = ruleChain.getName(); |
109 | - if (ruleChain.getType() != null) { | |
110 | - this.type = ruleChain.getType(); | |
111 | - } else { | |
112 | - this.type = RuleChainType.SYSTEM; | |
113 | - } | |
109 | + this.type = ruleChain.getType(); | |
114 | 110 | this.searchText = ruleChain.getName(); |
115 | 111 | this.firstRuleNodeId = DaoUtil.getId(ruleChain.getFirstRuleNodeId()); |
116 | 112 | this.root = ruleChain.isRoot(); |
... | ... | @@ -204,11 +200,7 @@ public class RuleChainEntity implements SearchTextEntity<RuleChain> { |
204 | 200 | ruleChain.setCreatedTime(UUIDs.unixTimestamp(id)); |
205 | 201 | ruleChain.setTenantId(new TenantId(tenantId)); |
206 | 202 | ruleChain.setName(name); |
207 | - if (type != null) { | |
208 | - ruleChain.setType(type); | |
209 | - } else { | |
210 | - ruleChain.setType(RuleChainType.SYSTEM); | |
211 | - } | |
203 | + ruleChain.setType(type); | |
212 | 204 | if (this.firstRuleNodeId != null) { |
213 | 205 | ruleChain.setFirstRuleNodeId(new RuleNodeId(this.firstRuleNodeId)); |
214 | 206 | } | ... | ... |
... | ... | @@ -103,11 +103,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT |
103 | 103 | } |
104 | 104 | this.tenantId = toString(DaoUtil.getId(ruleChain.getTenantId())); |
105 | 105 | this.name = ruleChain.getName(); |
106 | - if (ruleChain.getType() != null) { | |
107 | - this.type = ruleChain.getType(); | |
108 | - } else { | |
109 | - this.type = RuleChainType.SYSTEM; | |
110 | - } | |
106 | + this.type = ruleChain.getType(); | |
111 | 107 | this.searchText = ruleChain.getName(); |
112 | 108 | if (ruleChain.getFirstRuleNodeId() != null) { |
113 | 109 | this.firstRuleNodeId = UUIDConverter.fromTimeUUID(ruleChain.getFirstRuleNodeId().getId()); |
... | ... | @@ -141,11 +137,7 @@ public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchT |
141 | 137 | ruleChain.setCreatedTime(UUIDs.unixTimestamp(getId())); |
142 | 138 | ruleChain.setTenantId(new TenantId(toUUID(tenantId))); |
143 | 139 | ruleChain.setName(name); |
144 | - if (type != null) { | |
145 | - ruleChain.setType(type); | |
146 | - } else { | |
147 | - ruleChain.setType(RuleChainType.SYSTEM); | |
148 | - } | |
140 | + ruleChain.setType(type); | |
149 | 141 | if (firstRuleNodeId != null) { |
150 | 142 | ruleChain.setFirstRuleNodeId(new RuleNodeId(UUIDConverter.fromString(firstRuleNodeId))); |
151 | 143 | } | ... | ... |
... | ... | @@ -63,6 +63,8 @@ import java.util.Map; |
63 | 63 | import java.util.concurrent.ExecutionException; |
64 | 64 | import java.util.stream.Collectors; |
65 | 65 | |
66 | +import static org.thingsboard.server.dao.service.Validator.validateString; | |
67 | + | |
66 | 68 | /** |
67 | 69 | * Created by igor on 3/12/18. |
68 | 70 | */ |
... | ... | @@ -359,6 +361,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC |
359 | 361 | } |
360 | 362 | |
361 | 363 | @Override |
364 | + public TextPageData<RuleChain> findTenantRuleChainsByType(TenantId tenantId, RuleChainType type, TextPageLink pageLink) { | |
365 | + Validator.validateId(tenantId, "Incorrect tenant id for search rule chain request."); | |
366 | + Validator.validatePageLink(pageLink, "Incorrect PageLink object for search rule chain request."); | |
367 | + List<RuleChain> ruleChains = ruleChainDao.findRuleChainsByTenantIdAndType(tenantId.getId(), type, pageLink); | |
368 | + return new TextPageData<>(ruleChains, pageLink); | |
369 | + } | |
370 | + | |
371 | + @Override | |
362 | 372 | public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) { |
363 | 373 | Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request."); |
364 | 374 | RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId()); |
... | ... | @@ -514,7 +524,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC |
514 | 524 | @Override |
515 | 525 | protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { |
516 | 526 | if (StringUtils.isEmpty(ruleChain.getName())) { |
517 | - throw new DataValidationException("Rule chain name should be specified!."); | |
527 | + throw new DataValidationException("Rule chain name should be specified!"); | |
528 | + } | |
529 | + if (ruleChain.getType() == null) { | |
530 | + throw new DataValidationException("Rule chain type should be specified!"); | |
518 | 531 | } |
519 | 532 | if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) { |
520 | 533 | throw new DataValidationException("Rule chain should be assigned to tenant!"); | ... | ... |
... | ... | @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.page.TimePageLink; |
28 | 28 | import org.thingsboard.server.common.data.relation.EntityRelation; |
29 | 29 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
30 | 30 | import org.thingsboard.server.common.data.rule.RuleChain; |
31 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
31 | 32 | import org.thingsboard.server.dao.DaoUtil; |
32 | 33 | import org.thingsboard.server.dao.model.nosql.RuleChainEntity; |
33 | 34 | import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; |
... | ... | @@ -35,14 +36,19 @@ import org.thingsboard.server.dao.relation.RelationDao; |
35 | 36 | import org.thingsboard.server.dao.util.NoSqlDao; |
36 | 37 | |
37 | 38 | import java.util.ArrayList; |
39 | +import java.util.Arrays; | |
38 | 40 | import java.util.Collections; |
39 | 41 | import java.util.List; |
40 | 42 | import java.util.UUID; |
41 | 43 | |
42 | 44 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; |
45 | +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TENANT_ID_PROPERTY; | |
46 | +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY; | |
43 | 47 | import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; |
48 | +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; | |
44 | 49 | import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME; |
45 | 50 | import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY; |
51 | +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TYPE_PROPERTY; | |
46 | 52 | |
47 | 53 | @Component |
48 | 54 | @Slf4j |
... | ... | @@ -74,6 +80,17 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao<RuleCh |
74 | 80 | } |
75 | 81 | |
76 | 82 | @Override |
83 | + public List<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, TextPageLink pageLink) { | |
84 | + log.debug("Try to find rule chains by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink); | |
85 | + List<RuleChainEntity> ruleChainEntities = findPageWithTextSearch(new TenantId(tenantId), RULE_CHAIN_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, | |
86 | + Arrays.asList(eq(RULE_CHAIN_TYPE_PROPERTY, type), | |
87 | + eq(RULE_CHAIN_TENANT_ID_PROPERTY, tenantId)), | |
88 | + pageLink); | |
89 | + log.trace("Found rule chains [{}] by tenantId [{}] and pageLink [{}]", ruleChainEntities, tenantId, pageLink); | |
90 | + return DaoUtil.convertDataList(ruleChainEntities); | |
91 | + } | |
92 | + | |
93 | + @Override | |
77 | 94 | public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { |
78 | 95 | log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); |
79 | 96 | ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DASHBOARD, pageLink); | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; |
19 | 19 | import org.thingsboard.server.common.data.page.TextPageLink; |
20 | 20 | import org.thingsboard.server.common.data.page.TimePageLink; |
21 | 21 | import org.thingsboard.server.common.data.rule.RuleChain; |
22 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
22 | 23 | import org.thingsboard.server.dao.Dao; |
23 | 24 | |
24 | 25 | import java.util.List; |
... | ... | @@ -39,6 +40,16 @@ public interface RuleChainDao extends Dao<RuleChain> { |
39 | 40 | List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink); |
40 | 41 | |
41 | 42 | /** |
43 | + * Find rule chains by tenantId, type and page link. | |
44 | + * | |
45 | + * @param tenantId the tenantId | |
46 | + * @param type the type | |
47 | + * @param pageLink the page link | |
48 | + * @return the list of rule chain objects | |
49 | + */ | |
50 | + List<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, TextPageLink pageLink); | |
51 | + | |
52 | + /** | |
42 | 53 | * Find rule chains by tenantId, edgeId and page link. |
43 | 54 | * |
44 | 55 | * @param tenantId the tenantId | ... | ... |
... | ... | @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.page.TimePageLink; |
31 | 31 | import org.thingsboard.server.common.data.relation.EntityRelation; |
32 | 32 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
33 | 33 | import org.thingsboard.server.common.data.rule.RuleChain; |
34 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
34 | 35 | import org.thingsboard.server.dao.DaoUtil; |
35 | 36 | import org.thingsboard.server.dao.model.sql.RuleChainEntity; |
36 | 37 | import org.thingsboard.server.dao.relation.RelationDao; |
... | ... | @@ -68,6 +69,7 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R |
68 | 69 | |
69 | 70 | @Override |
70 | 71 | public List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink) { |
72 | + log.debug("Try to find rule chains by tenantId [{}] and pageLink [{}]", tenantId, pageLink); | |
71 | 73 | return DaoUtil.convertDataList(ruleChainRepository |
72 | 74 | .findByTenantId( |
73 | 75 | UUIDConverter.fromTimeUUID(tenantId), |
... | ... | @@ -77,6 +79,18 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R |
77 | 79 | } |
78 | 80 | |
79 | 81 | @Override |
82 | + public List<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, TextPageLink pageLink) { | |
83 | + log.debug("Try to find rule chains by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink); | |
84 | + return DaoUtil.convertDataList(ruleChainRepository | |
85 | + .findByTenantIdAndType( | |
86 | + UUIDConverter.fromTimeUUID(tenantId), | |
87 | + type, | |
88 | + Objects.toString(pageLink.getTextSearch(), ""), | |
89 | + pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()), | |
90 | + new PageRequest(0, pageLink.getLimit()))); | |
91 | + } | |
92 | + | |
93 | + @Override | |
80 | 94 | public ListenableFuture<List<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { |
81 | 95 | log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); |
82 | 96 | ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.RULE_CHAIN, pageLink); | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import org.springframework.data.domain.Pageable; |
19 | 19 | import org.springframework.data.jpa.repository.Query; |
20 | 20 | import org.springframework.data.repository.CrudRepository; |
21 | 21 | import org.springframework.data.repository.query.Param; |
22 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
22 | 23 | import org.thingsboard.server.dao.model.sql.RuleChainEntity; |
23 | 24 | import org.thingsboard.server.dao.util.SqlDao; |
24 | 25 | |
... | ... | @@ -35,4 +36,13 @@ public interface RuleChainRepository extends CrudRepository<RuleChainEntity, Str |
35 | 36 | @Param("idOffset") String idOffset, |
36 | 37 | Pageable pageable); |
37 | 38 | |
39 | + @Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " + | |
40 | + "AND rc.type = :type " + | |
41 | + "AND LOWER(rc.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + | |
42 | + "AND rc.id > :idOffset ORDER BY rc.id") | |
43 | + List<RuleChainEntity> findByTenantIdAndType(@Param("tenantId") String tenantId, | |
44 | + @Param("type") RuleChainType type, | |
45 | + @Param("searchText") String searchText, | |
46 | + @Param("idOffset") String idOffset, | |
47 | + Pageable pageable); | |
38 | 48 | } | ... | ... |
... | ... | @@ -597,22 +597,30 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_chain ( |
597 | 597 | id uuid, |
598 | 598 | tenant_id uuid, |
599 | 599 | name text, |
600 | + type text, | |
600 | 601 | search_text text, |
601 | 602 | first_rule_node_id uuid, |
602 | 603 | root boolean, |
603 | 604 | debug_mode boolean, |
604 | 605 | configuration text, |
605 | 606 | additional_info text, |
606 | - PRIMARY KEY (id, tenant_id) | |
607 | + PRIMARY KEY (id, tenant_id, type) | |
607 | 608 | ); |
608 | 609 | |
609 | 610 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS |
610 | 611 | SELECT * |
611 | 612 | from thingsboard.rule_chain |
612 | - WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL | |
613 | - PRIMARY KEY ( tenant_id, search_text, id ) | |
613 | + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL AND type IS NOT NULL | |
614 | + PRIMARY KEY ( tenant_id, search_text, id, type ) | |
614 | 615 | WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); |
615 | 616 | |
617 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_by_type_and_search_text AS | |
618 | + SELECT * | |
619 | + from thingsboard.rule_chain | |
620 | + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL AND type IS NOT NULL | |
621 | + PRIMARY KEY ( tenant_id, type, search_text, id ) | |
622 | + WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC ); | |
623 | + | |
616 | 624 | CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( |
617 | 625 | id uuid, |
618 | 626 | rule_chain_id uuid, | ... | ... |
... | ... | @@ -277,7 +277,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
277 | 277 | } |
278 | 278 | break; |
279 | 279 | case types.entityType.rulechain: |
280 | - promise = ruleChainService.getRuleChains(pageLink, config); | |
280 | + promise = ruleChainService.getRuleChains(pageLink, config, subType); | |
281 | 281 | break; |
282 | 282 | case types.entityType.dashboard: |
283 | 283 | if (user.authority === 'CUSTOMER_USER') { | ... | ... |
... | ... | @@ -46,7 +46,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co |
46 | 46 | |
47 | 47 | return service; |
48 | 48 | |
49 | - function getRuleChains (pageLink, config) { | |
49 | + function getRuleChains (pageLink, config, type) { | |
50 | 50 | var deferred = $q.defer(); |
51 | 51 | var url = '/api/ruleChains?limit=' + pageLink.limit; |
52 | 52 | if (angular.isDefined(pageLink.textSearch)) { |
... | ... | @@ -58,6 +58,9 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co |
58 | 58 | if (angular.isDefined(pageLink.textOffset)) { |
59 | 59 | url += '&textOffset=' + pageLink.textOffset; |
60 | 60 | } |
61 | + if (angular.isDefined(type) && type.length) { | |
62 | + url += '&type=' + type; | |
63 | + } | |
61 | 64 | $http.get(url, config).then(function success(response) { |
62 | 65 | deferred.resolve(prepareRuleChains(response.data)); |
63 | 66 | }, function fail() { | ... | ... |
... | ... | @@ -607,7 +607,7 @@ export default angular.module('thingsboard.types', []) |
607 | 607 | } |
608 | 608 | }, |
609 | 609 | systemRuleChainType: "SYSTEM", |
610 | - ruleChainTypes: ["SYSTEM", "EDGE"], | |
610 | + edgeRuleChainType: "EDGE", | |
611 | 611 | ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION", "EXTERNAL"], |
612 | 612 | ruleChainNodeComponent: { |
613 | 613 | type: 'RULE_CHAIN', | ... | ... |
... | ... | @@ -1391,10 +1391,11 @@ |
1391 | 1391 | "rulechain": { |
1392 | 1392 | "rulechain": "Rule chain", |
1393 | 1393 | "rulechains": "Rule chains", |
1394 | + "system-rulechains": "System Rule chains", | |
1395 | + "edge-rulechains": "Edge Rule chains", | |
1394 | 1396 | "root": "Root", |
1395 | 1397 | "delete": "Delete rule chain", |
1396 | 1398 | "name": "Name", |
1397 | - "type": "Type", | |
1398 | 1399 | "name-required": "Name is required.", |
1399 | 1400 | "description": "Description", |
1400 | 1401 | "add": "Add Rule Chain", | ... | ... |
... | ... | @@ -31,7 +31,7 @@ |
31 | 31 | <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
32 | 32 | <md-dialog-content> |
33 | 33 | <div class="md-dialog-content"> |
34 | - <tb-rule-chain rule-chain="vm.item" is-edit="true" rule-chain-scope="'tenant'" the-form="theForm"></tb-rule-chain> | |
34 | + <tb-rule-chain rule-chain="vm.item" is-edit="true" the-form="theForm"></tb-rule-chain> | |
35 | 35 | </div> |
36 | 36 | </md-dialog-content> |
37 | 37 | <md-dialog-actions layout="row"> | ... | ... |
... | ... | @@ -31,7 +31,7 @@ |
31 | 31 | <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> |
32 | 32 | <md-dialog-content> |
33 | 33 | <div class="md-dialog-content"> |
34 | - <tb-rule-node rule-node="vm.ruleNode" rule-chain-id="vm.ruleChainId" is-edit="true" the-form="theForm"></tb-rule-node> | |
34 | + <tb-rule-node rule-node="vm.ruleNode" rule-chain-id="vm.ruleChainId" rule-chain-type="vm.ruleChainType" is-edit="true" the-form="theForm"></tb-rule-node> | |
35 | 35 | </div> |
36 | 36 | </md-dialog-content> |
37 | 37 | <md-dialog-actions layout="row"> | ... | ... |
... | ... | @@ -50,14 +50,6 @@ |
50 | 50 | </div> |
51 | 51 | </md-input-container> |
52 | 52 | <md-input-container class="md-block"> |
53 | - <label translate>rulechain.type</label> | |
54 | - <md-select ng-disabled="$root.loading || !isEdit || ruleChainScope !== 'tenant' || ruleChain.root === true" name="type" ng-model="ruleChain.type"> | |
55 | - <md-option ng-repeat="ruleChainType in ruleChainTypes" value="{{ruleChainType}}"> | |
56 | - {{ruleChainType}} | |
57 | - </md-option> | |
58 | - </md-select> | |
59 | - </md-input-container> | |
60 | - <md-input-container class="md-block"> | |
61 | 53 | <md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulechain.debug-mode' | translate }}" |
62 | 54 | ng-model="ruleChain.debugMode">{{ 'rulechain.debug-mode' | translate }} |
63 | 55 | </md-checkbox> | ... | ... |
... | ... | @@ -1179,6 +1179,9 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
1179 | 1179 | function saveRuleChain() { |
1180 | 1180 | var saveRuleChainPromise; |
1181 | 1181 | if (vm.isImport) { |
1182 | + if (angular.isUndefined(vm.ruleChain.type)) { | |
1183 | + vm.ruleChain.type = types.systemRuleChainType; | |
1184 | + } | |
1182 | 1185 | saveRuleChainPromise = ruleChainService.saveRuleChain(vm.ruleChain); |
1183 | 1186 | } else { |
1184 | 1187 | saveRuleChainPromise = $q.when(vm.ruleChain); |
... | ... | @@ -1286,6 +1289,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
1286 | 1289 | ruleNode.configuration = angular.copy(ruleNode.component.configurationDescriptor.nodeDefinition.defaultConfiguration); |
1287 | 1290 | |
1288 | 1291 | var ruleChainId = vm.ruleChain.id ? vm.ruleChain.id.id : null; |
1292 | + var ruleChainType = vm.ruleChain.type ? vm.ruleChain.type : types.systemRuleChainType; | |
1289 | 1293 | |
1290 | 1294 | vm.enableHotKeys = false; |
1291 | 1295 | |
... | ... | @@ -1294,7 +1298,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
1294 | 1298 | controllerAs: 'vm', |
1295 | 1299 | templateUrl: addRuleNodeTemplate, |
1296 | 1300 | parent: angular.element($document[0].body), |
1297 | - locals: {ruleNode: ruleNode, ruleChainId: ruleChainId}, | |
1301 | + locals: {ruleNode: ruleNode, ruleChainId: ruleChainId, ruleChainType: ruleChainType}, | |
1298 | 1302 | fullscreen: true, |
1299 | 1303 | targetEvent: $event |
1300 | 1304 | }).then(function (ruleNode) { |
... | ... | @@ -1365,13 +1369,14 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time |
1365 | 1369 | } |
1366 | 1370 | |
1367 | 1371 | /*@ngInject*/ |
1368 | -export function AddRuleNodeController($scope, $mdDialog, ruleNode, ruleChainId, helpLinks) { | |
1372 | +export function AddRuleNodeController($scope, $mdDialog, ruleNode, ruleChainId, ruleChainType, helpLinks) { | |
1369 | 1373 | |
1370 | 1374 | var vm = this; |
1371 | 1375 | |
1372 | 1376 | vm.helpLinks = helpLinks; |
1373 | 1377 | vm.ruleNode = ruleNode; |
1374 | 1378 | vm.ruleChainId = ruleChainId; |
1379 | + vm.ruleChainType = ruleChainType; | |
1375 | 1380 | |
1376 | 1381 | vm.add = add; |
1377 | 1382 | vm.cancel = cancel; | ... | ... |
... | ... | @@ -26,12 +26,6 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog, |
26 | 26 | var template = $templateCache.get(ruleChainFieldsetTemplate); |
27 | 27 | element.html(template); |
28 | 28 | |
29 | - scope.ruleChainTypes = types.ruleChainTypes; | |
30 | - | |
31 | - if (angular.isDefined(scope.ruleChain) && scope.ruleChain != null && angular.isUndefined(scope.ruleChain.type)) { | |
32 | - scope.ruleChain.type = types.systemRuleChainType; | |
33 | - } | |
34 | - | |
35 | 29 | scope.onRuleChainIdCopied = function() { |
36 | 30 | toast.showSuccess($translate.instant('rulechain.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left'); |
37 | 31 | }; |
... | ... | @@ -44,7 +38,6 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog, |
44 | 38 | scope: { |
45 | 39 | ruleChain: '=', |
46 | 40 | isEdit: '=', |
47 | - ruleChainScope: '=', | |
48 | 41 | isReadOnly: '=', |
49 | 42 | theForm: '=', |
50 | 43 | onSetRootRuleChain: '&', | ... | ... |
... | ... | @@ -29,6 +29,15 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider |
29 | 29 | $stateProvider |
30 | 30 | .state('home.ruleChains', { |
31 | 31 | url: '/ruleChains', |
32 | + module: 'private', | |
33 | + auth: ['SYS_ADMIN', 'TENANT_ADMIN'], | |
34 | + redirectTo: 'home.ruleChains.system', | |
35 | + ncyBreadcrumb: { | |
36 | + label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}' | |
37 | + } | |
38 | + }) | |
39 | + .state('home.ruleChains.system', { | |
40 | + url: '/ruleChains/system', | |
32 | 41 | params: {'topIndex': 0}, |
33 | 42 | module: 'private', |
34 | 43 | auth: ['SYS_ADMIN', 'TENANT_ADMIN'], |
... | ... | @@ -41,11 +50,11 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider |
41 | 50 | }, |
42 | 51 | data: { |
43 | 52 | searchEnabled: true, |
44 | - pageTitle: 'rulechain.rulechains', | |
53 | + pageTitle: 'rulechain.system-rulechains', | |
45 | 54 | ruleChainsType: 'tenant' |
46 | 55 | }, |
47 | 56 | ncyBreadcrumb: { |
48 | - label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}' | |
57 | + label: '{"icon": "settings_ethernet", "label": "rulechain.system-rulechains"}' | |
49 | 58 | } |
50 | 59 | }).state('home.ruleChains.ruleChain', { |
51 | 60 | url: '/:ruleChainId', |
... | ... | @@ -124,8 +133,64 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider |
124 | 133 | ncyBreadcrumb: { |
125 | 134 | label: '{"icon": "settings_ethernet", "label": "{{ (\'rulechain.import\' | translate) + \': \'+ vm.ruleChain.name }}", "translate": "false"}' |
126 | 135 | } |
127 | - }) | |
128 | - .state('home.edges.ruleChains', { | |
136 | + }).state('home.ruleChains.edge', { | |
137 | + url: '/ruleChains/edge', | |
138 | + params: {'topIndex': 0}, | |
139 | + module: 'private', | |
140 | + auth: ['TENANT_ADMIN'], | |
141 | + views: { | |
142 | + "content@home": { | |
143 | + templateUrl: ruleChainsTemplate, | |
144 | + controllerAs: 'vm', | |
145 | + controller: 'RuleChainsController' | |
146 | + } | |
147 | + }, | |
148 | + data: { | |
149 | + searchEnabled: true, | |
150 | + pageTitle: 'rulechain.edge-rulechains', | |
151 | + ruleChainsType: 'edges' | |
152 | + }, | |
153 | + ncyBreadcrumb: { | |
154 | + label: '{"icon": "settings_ethernet", "label": "rulechain.edge-rulechains"}' | |
155 | + } | |
156 | + }).state('home.ruleChains.edge.ruleChain', { | |
157 | + url: '/:ruleChainId', | |
158 | + reloadOnSearch: false, | |
159 | + module: 'private', | |
160 | + auth: ['SYS_ADMIN', 'TENANT_ADMIN'], | |
161 | + views: { | |
162 | + "content@home": { | |
163 | + templateUrl: ruleChainTemplate, | |
164 | + controller: 'RuleChainController', | |
165 | + controllerAs: 'vm' | |
166 | + } | |
167 | + }, | |
168 | + resolve: { | |
169 | + ruleChain: | |
170 | + /*@ngInject*/ | |
171 | + function($stateParams, ruleChainService) { | |
172 | + return ruleChainService.getRuleChain($stateParams.ruleChainId); | |
173 | + }, | |
174 | + ruleChainMetaData: | |
175 | + /*@ngInject*/ | |
176 | + function($stateParams, ruleChainService) { | |
177 | + return ruleChainService.getRuleChainMetaData($stateParams.ruleChainId); | |
178 | + }, | |
179 | + ruleNodeComponents: | |
180 | + /*@ngInject*/ | |
181 | + function($stateParams, ruleChainService) { | |
182 | + return ruleChainService.getRuleNodeComponents(); | |
183 | + } | |
184 | + }, | |
185 | + data: { | |
186 | + import: false, | |
187 | + searchEnabled: false, | |
188 | + pageTitle: 'edge.rulechain' | |
189 | + }, | |
190 | + ncyBreadcrumb: { | |
191 | + label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}' | |
192 | + } | |
193 | + }).state('home.edges.ruleChains', { | |
129 | 194 | url: '/:edgeId/ruleChains', |
130 | 195 | params: {'topIndex': 0}, |
131 | 196 | module: 'private', |
... | ... | @@ -138,15 +203,14 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider |
138 | 203 | } |
139 | 204 | }, |
140 | 205 | data: { |
141 | - ruleChainsType: 'edge', | |
142 | 206 | searchEnabled: true, |
143 | - pageTitle: 'edge.rulechains' | |
207 | + pageTitle: 'edge.rulechains', | |
208 | + ruleChainsType: 'edge' | |
144 | 209 | }, |
145 | 210 | ncyBreadcrumb: { |
146 | 211 | label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}' |
147 | 212 | } |
148 | - }) | |
149 | - .state('home.edges.ruleChains.ruleChain', { | |
213 | + }).state('home.edges.ruleChains.ruleChain', { | |
150 | 214 | url: '/:ruleChainId', |
151 | 215 | reloadOnSearch: false, |
152 | 216 | module: 'private', |
... | ... | @@ -181,7 +245,7 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider |
181 | 245 | pageTitle: 'edge.rulechain' |
182 | 246 | }, |
183 | 247 | ncyBreadcrumb: { |
184 | - label: '{"icon": "settings_ethernet", "label": "edge.rulechain"}' | |
248 | + label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}' | |
185 | 249 | } |
186 | 250 | }); |
187 | 251 | } |
\ No newline at end of file | ... | ... |
... | ... | @@ -116,7 +116,7 @@ export default function RuleChainsController(ruleChainService, userService, edge |
116 | 116 | |
117 | 117 | if (vm.ruleChainsScope === 'tenant') { |
118 | 118 | fetchRuleChainsFunction = function (pageLink) { |
119 | - return fetchRuleChains(pageLink); | |
119 | + return fetchRuleChains(pageLink, 'SYSTEM'); | |
120 | 120 | }; |
121 | 121 | deleteRuleChainFunction = function (ruleChainId) { |
122 | 122 | return deleteRuleChain(ruleChainId); |
... | ... | @@ -134,6 +134,57 @@ export default function RuleChainsController(ruleChainService, userService, edge |
134 | 134 | |
135 | 135 | ruleChainActionsList.push({ |
136 | 136 | onAction: function ($event, item) { |
137 | + vm.grid.deleteItem($event, item); | |
138 | + }, | |
139 | + name: function() { return $translate.instant('action.delete') }, | |
140 | + details: function() { return $translate.instant('rulechain.delete') }, | |
141 | + icon: "delete", | |
142 | + isEnabled: isNonRootRuleChain | |
143 | + }); | |
144 | + | |
145 | + ruleChainGroupActionsList.push( | |
146 | + { | |
147 | + onAction: function ($event) { | |
148 | + vm.grid.deleteItems($event); | |
149 | + }, | |
150 | + name: function() { return $translate.instant('rulechain.delete-rulechains') }, | |
151 | + details: deleteRuleChainsActionTitle, | |
152 | + icon: "delete" | |
153 | + } | |
154 | + ); | |
155 | + | |
156 | + vm.ruleChainGridConfig.addItemActions = []; | |
157 | + vm.ruleChainGridConfig.addItemActions.push({ | |
158 | + onAction: function ($event) { | |
159 | + vm.grid.addItem($event); | |
160 | + }, | |
161 | + name: function() { return $translate.instant('action.create') }, | |
162 | + details: function() { return $translate.instant('rulechain.create-new-rulechain') }, | |
163 | + icon: "insert_drive_file" | |
164 | + }); | |
165 | + vm.ruleChainGridConfig.addItemActions.push({ | |
166 | + onAction: function ($event) { | |
167 | + importExport.importRuleChain($event).then( | |
168 | + function(ruleChainImport) { | |
169 | + $state.go('home.ruleChains.importRuleChain', {ruleChainImport:ruleChainImport}); | |
170 | + } | |
171 | + ); | |
172 | + }, | |
173 | + name: function() { return $translate.instant('action.import') }, | |
174 | + details: function() { return $translate.instant('rulechain.import') }, | |
175 | + icon: "file_upload" | |
176 | + }); | |
177 | + | |
178 | + } else if (vm.ruleChainsScope === 'edges') { | |
179 | + fetchRuleChainsFunction = function (pageLink) { | |
180 | + return fetchRuleChains(pageLink, 'EDGE'); | |
181 | + }; | |
182 | + deleteRuleChainFunction = function (ruleChainId) { | |
183 | + return deleteRuleChain(ruleChainId); | |
184 | + }; | |
185 | + | |
186 | + ruleChainActionsList.push({ | |
187 | + onAction: function ($event, item) { | |
137 | 188 | manageAssignedEdges($event, item); |
138 | 189 | }, |
139 | 190 | name: function() { return $translate.instant('action.assign') }, |
... | ... | @@ -209,7 +260,6 @@ export default function RuleChainsController(ruleChainService, userService, edge |
209 | 260 | details: function() { return $translate.instant('rulechain.import') }, |
210 | 261 | icon: "file_upload" |
211 | 262 | }); |
212 | - | |
213 | 263 | } else if (vm.ruleChainsScope === 'edge') { |
214 | 264 | fetchRuleChainsFunction = function (pageLink) { |
215 | 265 | return ruleChainService.getEdgeRuleChains(edgeId, pageLink); |
... | ... | @@ -291,11 +341,18 @@ export default function RuleChainsController(ruleChainService, userService, edge |
291 | 341 | vm.grid = grid; |
292 | 342 | } |
293 | 343 | |
294 | - function fetchRuleChains(pageLink) { | |
295 | - return ruleChainService.getRuleChains(pageLink); | |
344 | + function fetchRuleChains(pageLink, type) { | |
345 | + return ruleChainService.getRuleChains(pageLink, null, type); | |
296 | 346 | } |
297 | 347 | |
298 | 348 | function saveRuleChain(ruleChain) { |
349 | + if (angular.isUndefined(ruleChain.type)) { | |
350 | + if (vm.ruleChainsScope === 'edges') { | |
351 | + ruleChain.type = types.edgeRuleChainType; | |
352 | + } else { | |
353 | + ruleChain.type = types.systemRuleChainType; | |
354 | + } | |
355 | + } | |
299 | 356 | return ruleChainService.saveRuleChain(ruleChain); |
300 | 357 | } |
301 | 358 | |
... | ... | @@ -303,10 +360,11 @@ export default function RuleChainsController(ruleChainService, userService, edge |
303 | 360 | if ($event) { |
304 | 361 | $event.stopPropagation(); |
305 | 362 | } |
363 | + | |
306 | 364 | if (vm.ruleChainsScope === 'edge') { |
307 | - $state.go('home.edges.ruleChains.ruleChain', { | |
308 | - ruleChainId: ruleChain.id.id | |
309 | - }); | |
365 | + $state.go('home.edges.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id, edgeId: vm.edge.id.id}); | |
366 | + } else if (vm.ruleChainsScope === 'edges') { | |
367 | + $state.go('home.ruleChains.edge.ruleChain', {ruleChainId: ruleChain.id.id}); | |
310 | 368 | } else { |
311 | 369 | $state.go('home.ruleChains.ruleChain', {ruleChainId: ruleChain.id.id}); |
312 | 370 | } | ... | ... |
... | ... | @@ -53,6 +53,7 @@ |
53 | 53 | tb-required="true" |
54 | 54 | exclude-entity-ids="[ruleChainId]" |
55 | 55 | entity-type="types.entityType.rulechain" |
56 | + entity-subtype="ruleChainType" | |
56 | 57 | ng-model="params.targetRuleChainId"> |
57 | 58 | </tb-entity-autocomplete> |
58 | 59 | <md-input-container class="md-block"> | ... | ... |
... | ... | @@ -156,9 +156,24 @@ function Menu(userService, $state, $rootScope) { |
156 | 156 | }, |
157 | 157 | { |
158 | 158 | name: 'rulechain.rulechains', |
159 | - type: 'link', | |
159 | + type: 'toggle', | |
160 | 160 | state: 'home.ruleChains', |
161 | - icon: 'settings_ethernet' | |
161 | + height: '80px', | |
162 | + icon: 'settings_ethernet', | |
163 | + pages: [ | |
164 | + { | |
165 | + name: 'rulechain.system-rulechains', | |
166 | + type: 'link', | |
167 | + state: 'home.ruleChains.system', | |
168 | + icon: 'settings_ethernet' | |
169 | + }, | |
170 | + { | |
171 | + name: 'rulechain.edge-rulechains', | |
172 | + type: 'link', | |
173 | + state: 'home.ruleChains.edge', | |
174 | + icon: 'router' | |
175 | + } | |
176 | + ] | |
162 | 177 | }, |
163 | 178 | { |
164 | 179 | name: 'customer.customers', |
... | ... | @@ -214,9 +229,14 @@ function Menu(userService, $state, $rootScope) { |
214 | 229 | name: 'rulechain.management', |
215 | 230 | places: [ |
216 | 231 | { |
217 | - name: 'rulechain.rulechains', | |
232 | + name: 'rulechain.system-rulechains', | |
218 | 233 | icon: 'settings_ethernet', |
219 | 234 | state: 'home.ruleChains' |
235 | + }, | |
236 | + { | |
237 | + name: 'rulechain.edge-rulechains', | |
238 | + icon: 'router', | |
239 | + state: 'home.edgesRuleChains' | |
220 | 240 | } |
221 | 241 | ] |
222 | 242 | }, | ... | ... |