Browse Source

Merge remote-tracking branch 'origin/develop/2.6-edge' into develop/3.3-edge

pull/3811/head
Volodymyr Babak 6 years ago
parent
commit
b45bee7843
  1. 16
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  2. 5
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java
  3. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseProcessor.java
  4. 101
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java
  5. 60
      application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java
  6. 2
      common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventActionType.java
  7. 3
      common/edge-api/src/main/proto/edge.proto
  8. 19
      rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java
  9. 20
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  10. 277
      ui/src/app/api/edge.service.js
  11. 123
      ui/src/app/asset/add-assets-to-edge.controller.js
  12. 77
      ui/src/app/asset/add-assets-to-edge.tpl.html
  13. 123
      ui/src/app/dashboard/add-dashboards-to-edge.controller.js
  14. 77
      ui/src/app/dashboard/add-dashboards-to-edge.tpl.html
  15. 123
      ui/src/app/device/add-devices-to-edge.controller.js
  16. 77
      ui/src/app/device/add-devices-to-edge.tpl.html
  17. 46
      ui/src/app/edge/add-edge.tpl.html
  18. 123
      ui/src/app/edge/add-edges-to-customer.controller.js
  19. 77
      ui/src/app/edge/add-edges-to-customer.tpl.html
  20. 123
      ui/src/app/edge/assign-to-customer.controller.js
  21. 76
      ui/src/app/edge/assign-to-customer.tpl.html
  22. 23
      ui/src/app/edge/edge-card.tpl.html
  23. 152
      ui/src/app/edge/edge-fieldset.tpl.html
  24. 695
      ui/src/app/edge/edge.controller.js
  25. 118
      ui/src/app/edge/edge.directive.js
  26. 241
      ui/src/app/edge/edge.routes.js
  27. 82
      ui/src/app/edge/edges.tpl.html
  28. 43
      ui/src/app/edge/index.js
  29. 129
      ui/src/app/edge/set-root-rule-chain-to-edges.controller.js
  30. 76
      ui/src/app/edge/set-root-rule-chain-to-edges.tpl.html
  31. 123
      ui/src/app/entity-view/add-entity-views-to-edge.controller.js
  32. 77
      ui/src/app/entity-view/add-entity-views-to-edge.tpl.html
  33. 23
      ui/src/app/event/event-header-edge-event.tpl.html
  34. 37
      ui/src/app/event/event-row-edge-event.tpl.html
  35. 122
      ui/src/app/rulechain/add-rulechains-to-edge.controller.js
  36. 77
      ui/src/app/rulechain/add-rulechains-to-edge.tpl.html

16
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@ -342,8 +343,8 @@ public final class EdgeGrpcSession implements Closeable {
case CREDENTIALS_REQUEST:
downlinkMsg = processCredentialsRequestMessage(edgeEvent);
break;
case ENTITY_EXISTS_REQUEST:
downlinkMsg = processEntityExistsRequestMessage(edgeEvent);
case ENTITY_MERGE_REQUEST:
downlinkMsg = processEntityMergeRequestMessage(edgeEvent);
break;
case RPC_CALL:
downlinkMsg = processRpcCallMsg(edgeEvent);
@ -359,13 +360,18 @@ public final class EdgeGrpcSession implements Closeable {
return result;
}
private DownlinkMsg processEntityExistsRequestMessage(EdgeEvent edgeEvent) {
private DownlinkMsg processEntityMergeRequestMessage(EdgeEvent edgeEvent) {
DownlinkMsg downlinkMsg = null;
if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) {
DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());
Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), deviceId);
CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device);
DeviceUpdateMsg d = ctx.getDeviceMsgConstructor().constructDeviceUpdatedMsg(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE, device, customerId);
String conflictName = null;
if(edgeEvent.getBody() != null) {
conflictName = edgeEvent.getBody().get("conflictName").asText();
}
DeviceUpdateMsg d = ctx.getDeviceMsgConstructor()
.constructDeviceUpdatedMsg(UpdateMsgType.ENTITY_MERGE_RPC_MESSAGE, device, customerId, conflictName);
downlinkMsg = DownlinkMsg.newBuilder()
.addAllDeviceUpdateMsg(Collections.singletonList(d))
.build();
@ -504,7 +510,7 @@ public final class EdgeGrpcSession implements Closeable {
if (device != null) {
CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device);
DeviceUpdateMsg deviceUpdateMsg =
ctx.getDeviceMsgConstructor().constructDeviceUpdatedMsg(msgType, device, customerId);
ctx.getDeviceMsgConstructor().constructDeviceUpdatedMsg(msgType, device, customerId, null);
downlinkMsg = DownlinkMsg.newBuilder()
.addAllDeviceUpdateMsg(Collections.singletonList(deviceUpdateMsg))
.build();

5
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java

@ -38,7 +38,7 @@ public class DeviceMsgConstructor {
protected static final ObjectMapper mapper = new ObjectMapper();
public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, CustomerId customerId) {
public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, CustomerId customerId, String conflictName) {
DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(device.getId().getId().getMostSignificantBits())
@ -55,6 +55,9 @@ public class DeviceMsgConstructor {
if (device.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo()));
}
if (conflictName != null) {
builder.setConflictName(conflictName);
}
return builder.build();
}

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseProcessor.java

@ -37,6 +37,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.user.UserService;
@ -64,6 +65,9 @@ public abstract class BaseProcessor {
@Autowired
protected EntityViewService entityViewService;
@Autowired
protected EdgeService edgeService;
@Autowired
protected CustomerService customerService;

101
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java

@ -17,12 +17,14 @@ package org.thingsboard.server.service.edge.rpc.processor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.server.common.data.DataConstants;
@ -52,6 +54,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock;
@ -64,38 +67,62 @@ public class DeviceProcessor extends BaseProcessor {
public ListenableFuture<Void> onDeviceUpdate(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
log.trace("[{}] onDeviceUpdate [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getName());
DeviceId edgeDeviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
switch (deviceUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
String deviceName = deviceUpdateMsg.getName();
Device device = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName);
if (device != null) {
log.info("[{}] Device with name '{}' already exists on the cloud. Updating id of device entity on the edge", tenantId, deviceName);
if (!device.getId().equals(edgeDeviceId)) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_EXISTS_REQUEST, device.getId(), null);
}
} else {
Device deviceById = deviceService.findDeviceById(edge.getTenantId(), edgeDeviceId);
if (deviceById != null) {
log.info("[{}] Device ID [{}] already used by other device on the cloud. Creating new device and replacing device entity on the edge", tenantId, edgeDeviceId.getId());
device = createDevice(tenantId, edge, deviceUpdateMsg);
ListenableFuture<List<EdgeId>> future = edgeService.findRelatedEdgeIdsByEntityId(tenantId, device.getId());
SettableFuture<Void> futureToSet = SettableFuture.create();
Futures.addCallback(future, new FutureCallback<List<EdgeId>>() {
@Override
public void onSuccess(@Nullable List<EdgeId> edgeIds) {
boolean update = false;
if (edgeIds != null && !edgeIds.isEmpty()) {
if (edgeIds.contains(edge.getId())) {
update = true;
}
}
Device device;
if (update) {
log.info("[{}] Device with name '{}' already exists on the cloud, and related to this edge [{}]. " +
"deviceUpdateMsg [{}], Updating device", tenantId, deviceName, edge.getId(), deviceUpdateMsg);
updateDevice(tenantId, edge, deviceUpdateMsg);
} else {
log.info("[{}] Device with name '{}' already exists on the cloud, but not related to this edge [{}]. deviceUpdateMsg [{}]." +
"Creating a new device with random prefix and relate to this edge", tenantId, deviceName, edge.getId(), deviceUpdateMsg);
String newDeviceName = deviceUpdateMsg.getName() + "_" + RandomStringUtils.randomAlphabetic(15);
device = createDevice(tenantId, edge, deviceUpdateMsg, newDeviceName);
ObjectNode body = mapper.createObjectNode();
body.put("conflictName", deviceName);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), body);
deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());
}
futureToSet.set(null);
}
// TODO: voba - properly handle device credentials from edge to cloud
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_EXISTS_REQUEST, device.getId(), null);
} else {
device = createDevice(tenantId, edge, deviceUpdateMsg);
}
@Override
public void onFailure(Throwable t) {
log.error("[{}] Failed to get related edge ids by device id [{}], edge [{}]", tenantId, deviceUpdateMsg, edge.getId(), t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
return futureToSet;
} else {
log.info("[{}] Creating new device and replacing device entity on the edge [{}]", tenantId, deviceUpdateMsg);
device = createDevice(tenantId, edge, deviceUpdateMsg, deviceUpdateMsg.getName());
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), null);
deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());
}
// TODO: voba - assign device only in case device is not assigned yet. Missing functionality to check this relation prior assignment
deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());
break;
case ENTITY_UPDATED_RPC_MESSAGE:
updateDevice(tenantId, edge, deviceUpdateMsg);
break;
case ENTITY_DELETED_RPC_MESSAGE:
Device deviceToDelete = deviceService.findDeviceById(tenantId, edgeDeviceId);
DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
Device deviceToDelete = deviceService.findDeviceById(tenantId, deviceId);
if (deviceToDelete != null) {
deviceService.unassignDeviceFromEdge(tenantId, edgeDeviceId, edge.getId());
deviceService.unassignDeviceFromEdge(tenantId, deviceId, edge.getId());
}
break;
case UNRECOGNIZED:
@ -105,7 +132,6 @@ public class DeviceProcessor extends BaseProcessor {
return Futures.immediateFuture(null);
}
public ListenableFuture<Void> onDeviceCredentialsUpdate(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
log.debug("Executing onDeviceCredentialsUpdate, deviceCredentialsUpdateMsg [{}]", deviceCredentialsUpdateMsg);
DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB()));
@ -133,37 +159,34 @@ public class DeviceProcessor extends BaseProcessor {
private void updateDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
Device device = deviceService.findDeviceById(tenantId, deviceId);
device.setName(deviceUpdateMsg.getName());
device.setType(deviceUpdateMsg.getType());
device.setLabel(deviceUpdateMsg.getLabel());
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()));
deviceService.saveDevice(device);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null);
if (device != null) {
device.setName(deviceUpdateMsg.getName());
device.setType(deviceUpdateMsg.getType());
device.setLabel(deviceUpdateMsg.getLabel());
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()));
deviceService.saveDevice(device);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null);
} else {
log.warn("[{}] can't find device [{}], edge [{}]", tenantId, deviceUpdateMsg, edge.getId());
}
}
private Device createDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
private Device createDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg, String deviceName) {
Device device;
try {
deviceCreationLock.lock();
log.debug("[{}] Creating device entity [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getName());
// DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
device = new Device();
// device.setId(deviceId);
device.setTenantId(edge.getTenantId());
device.setCustomerId(edge.getCustomerId());
device.setName(deviceUpdateMsg.getName());
device.setName(deviceName);
device.setType(deviceUpdateMsg.getType());
device.setLabel(deviceUpdateMsg.getLabel());
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()));
device = deviceService.saveDevice(device);
// TODO: voba - is this still required?
// createDeviceCredentials(device);
createRelationFromEdge(tenantId, edge.getId(), device.getId());
deviceStateService.onDeviceAdded(device);
pushDeviceCreatedEventToRuleEngine(tenantId, edge, device);
// saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null);
} finally {
deviceCreationLock.unlock();
}
@ -179,14 +202,6 @@ public class DeviceProcessor extends BaseProcessor {
relationService.saveRelation(tenantId, relation);
}
private void createDeviceCredentials(Device device) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(device.getId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
}
private void pushDeviceCreatedEventToRuleEngine(TenantId tenantId, Edge edge, Device device) {
try {
DeviceId deviceId = device.getId();

60
application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java

@ -26,6 +26,7 @@ import com.google.protobuf.AbstractMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -962,6 +963,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
private void testSendMessagesToCloud() throws Exception {
log.info("Sending messages to cloud");
sendDevice();
sendDeviceWithNameThatAlreadyExistsOnCloud();
sendRelationRequest();
sendAlarm();
sendTelemetry();
@ -972,8 +974,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
sendDeviceCredentialsRequest();
sendDeviceRpcResponse();
sendDeviceCredentialsUpdate();
// TODO: voba - fix this test
// sendAttributesRequest();
sendAttributesRequest();
log.info("Messages were sent successfully");
}
@ -991,17 +992,64 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build());
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1);
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
edgeImitator.waitForResponses();
edgeImitator.waitForMessages();
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg);
DeviceUpdateMsg latestDeviceUpdateMsg = (DeviceUpdateMsg) latestMessage;
Assert.assertEquals("Edge Device 2", latestDeviceUpdateMsg.getName());
UUID newDeviceId = new UUID(latestDeviceUpdateMsg.getIdMSB(), latestDeviceUpdateMsg.getIdLSB());
Device device = doGet("/api/device/" + newDeviceId, Device.class);
Assert.assertNotNull(device);
Assert.assertEquals("Edge Device 2", device.getName());
}
private void sendDeviceWithNameThatAlreadyExistsOnCloud() throws Exception {
String deviceOnCloudName = RandomStringUtils.randomAlphanumeric(15);
Device deviceOnCloud = saveDevice(deviceOnCloudName);
UUID uuid = Uuids.timeBased();
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder();
deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits());
deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits());
deviceUpdateMsgBuilder.setName(deviceOnCloudName);
deviceUpdateMsgBuilder.setType("test");
deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
testAutoGeneratedCodeByProtobuf(deviceUpdateMsgBuilder);
uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build());
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1);
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
edgeImitator.waitForResponses();
edgeImitator.waitForMessages();
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg);
DeviceUpdateMsg latestDeviceUpdateMsg = (DeviceUpdateMsg) latestMessage;
Assert.assertNotEquals(deviceOnCloudName, latestDeviceUpdateMsg.getName());
Assert.assertEquals(deviceOnCloudName, latestDeviceUpdateMsg.getConflictName());
// TODO: voba - add proper validation once DeviceProcessor
UUID newDeviceId = new UUID(latestDeviceUpdateMsg.getIdMSB(), latestDeviceUpdateMsg.getIdLSB());
// Device device = doGet("/api/device/" + uuid.toString(), Device.class);
// Assert.assertNotNull(device);
// Assert.assertEquals("Edge Device 2", device.getName());
Assert.assertNotEquals(deviceOnCloud.getId().getId(), newDeviceId);
Device device = doGet("/api/device/" + newDeviceId, Device.class);
Assert.assertNotNull(device);
Assert.assertNotEquals(deviceOnCloudName, device.getName());
}
private void sendRelationRequest() throws Exception {

2
common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventActionType.java

@ -34,5 +34,5 @@ public enum EdgeEventActionType {
ASSIGNED_TO_EDGE,
UNASSIGNED_FROM_EDGE,
CREDENTIALS_REQUEST,
ENTITY_EXISTS_REQUEST
ENTITY_MERGE_REQUEST
}

3
common/edge-api/src/main/proto/edge.proto

@ -97,7 +97,7 @@ enum UpdateMsgType {
ENTITY_DELETED_RPC_MESSAGE = 2;
ALARM_ACK_RPC_MESSAGE = 3;
ALARM_CLEAR_RPC_MESSAGE = 4;
DEVICE_CONFLICT_RPC_MESSAGE = 5;
ENTITY_MERGE_RPC_MESSAGE = 5;
}
message EntityDataProto {
@ -182,6 +182,7 @@ message DeviceUpdateMsg {
string type = 7;
string label = 8;
string additionalInfo = 9;
string conflictName = 10;
}
message DeviceCredentialsUpdateMsg {

19
rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java

@ -109,6 +109,7 @@ import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainData;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.data.security.model.SecuritySettings;
@ -120,6 +121,7 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle;
import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -700,22 +702,31 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable {
}
public List<ComponentDescriptor> getComponentDescriptorsByType(ComponentType componentType) {
return getComponentDescriptorsByType(componentType, RuleChainType.CORE);
}
public List<ComponentDescriptor> getComponentDescriptorsByType(ComponentType componentType, RuleChainType ruleChainType) {
return restTemplate.exchange(
baseURL + "/api/components?componentType={componentType}",
baseURL + "/api/components/" + componentType.name() + "/?ruleChainType={ruleChainType}",
HttpMethod.GET, HttpEntity.EMPTY,
new ParameterizedTypeReference<List<ComponentDescriptor>>() {
},
componentType).getBody();
ruleChainType).getBody();
}
public List<ComponentDescriptor> getComponentDescriptorsByTypes(List<ComponentType> componentTypes) {
return getComponentDescriptorsByTypes(componentTypes, RuleChainType.CORE);
}
public List<ComponentDescriptor> getComponentDescriptorsByTypes(List<ComponentType> componentTypes, RuleChainType ruleChainType) {
return restTemplate.exchange(
baseURL + "/api/components?componentTypes={componentTypes}",
baseURL + "/api/components?componentTypes={componentTypes}&ruleChainType={ruleChainType}",
HttpMethod.GET,
HttpEntity.EMPTY,
new ParameterizedTypeReference<List<ComponentDescriptor>>() {
},
listEnumToString(componentTypes))
listEnumToString(componentTypes),
ruleChainType)
.getBody();
}

20
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1117,6 +1117,7 @@
"edge": {
"edge": "Edge",
"edges": "Edges",
"edge-file": "Edge file",
"management": "Edge management",
"no-edges-matching": "No edges matching '{{entity}}' were found.",
"add": "Add Edge",
@ -1173,17 +1174,19 @@
"make-private-edge-text": "After the confirmation the edge and all its data will be made private and won't be accessible by others.",
"import": "Import edge",
"label": "Label",
"load-entity-error": "Entity not found. Failed to load info",
"load-entity-error": "Failed to load data. Entity not found or has been deleted.",
"assign-new-edge": "Assign new edge",
"manage-edge-dashboards": "Manage edge dashboards",
"unassign-from-edge": "Unassign from edge",
"dashboards": "Edge Dashboards",
"manage-edge-rulechains": "Manage edge rule chains",
"rulechains": "Edge Rule Chains",
"rulechain": "Edge Rule Chain",
"edge-key": "Edge key",
"copy-edge-key": "Copy Edge key",
"copy-edge-key": "Copy edge key",
"edge-key-copied-message": "Edge key has been copied to clipboard",
"edge-secret": "Edge secret",
"copy-edge-secret": "Copy Edge secret",
"copy-edge-secret": "Copy edge secret",
"edge-secret-copied-message": "Edge secret has been copied to clipboard",
"manage-edge-assets": "Manage edge assets",
"manage-edge-devices": "Manage edge devices",
@ -1191,22 +1194,23 @@
"assets": "Edge assets",
"devices": "Edge devices",
"entity-views": "Edge entity views",
"set-root-rulechain-text": "Please select root rule chain for edge(s)",
"set-root-rulechain-to-edges": "Set root rule chain for Edge(s)",
"set-root-rulechain-to-edges-text": "Set root rule chain for { count, plural, 1 {1 edge} other {# edges} }",
"set-root-rule-chain-text": "Please select root rule chain for edge(s)",
"set-root-rule-chain-to-edges": "Set root rule chain for Edge(s)",
"set-root-rule-chain-to-edges-text": "Set root rule chain for { count, plural, 1 {1 edge} other {# edges} }",
"status": "Received by edge",
"success": "Deployed",
"failed": "Pending",
"search": "Search edges",
"selected-edges": "{ count, plural, 1 {1 edge} other {# edges} } selected",
"enter-edge-type": "Enter entity view type",
"any-edge": "Any edge",
"no-edge-types-matching": "No edge types matching '{{entitySubtype}}' were found.",
"edge-type-list-empty": "No device types selected.",
"edge-types": "Edge types",
"dashboard": "Edge dashboard",
"unassign-edges-action-title": "Unassign { count, plural, 1 {1 edge} other {# edges} } from customer",
"enter-edge-type": "Enter edge type"
"enter-edge-type": "Enter edge type",
"deployed": "Deployed",
"pending": "Pending"
},
"error": {
"unable-to-connect": "Unable to connect to the server! Please check your internet connection.",

277
ui/src/app/api/edge.service.js

@ -1,277 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export default angular.module('thingsboard.api.edge', [])
.factory('edgeService', EdgeService)
.name;
/*@ngInject*/
function EdgeService($http, $q, customerService) {
var service = {
getEdges: getEdges,
getEdgesByIds: getEdgesByIds,
getEdge: getEdge,
deleteEdge: deleteEdge,
saveEdge: saveEdge,
getEdgeTypes: getEdgeTypes,
getTenantEdges: getTenantEdges,
getCustomerEdges: getCustomerEdges,
assignEdgeToCustomer: assignEdgeToCustomer,
unassignEdgeFromCustomer: unassignEdgeFromCustomer,
makeEdgePublic: makeEdgePublic,
setRootRuleChain: setRootRuleChain,
getEdgeEvents: getEdgeEvents,
syncEdge: syncEdge
};
return service;
function getEdges(pageLink, config) {
var deferred = $q.defer();
var url = '/api/edges?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
url += '&textSearch=' + pageLink.textSearch;
}
if (angular.isDefined(pageLink.idOffset)) {
url += '&idOffset=' + pageLink.idOffset;
}
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
}
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getEdgesByIds(edgeIds, config) {
var deferred = $q.defer();
var ids = '';
for (var i=0;i<edgeIds.length;i++) {
if (i>0) {
ids += ',';
}
ids += edgeIds[i];
}
var url = '/api/edges?edgeIds=' + ids;
$http.get(url, config).then(function success(response) {
var entities = response.data;
entities.sort(function (entity1, entity2) {
var id1 = entity1.id.id;
var id2 = entity2.id.id;
var index1 = edgeIds.indexOf(id1);
var index2 = edgeIds.indexOf(id2);
return index1 - index2;
});
deferred.resolve(entities);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getEdge(edgeId, config) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId;
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail(response) {
deferred.reject(response.data);
});
return deferred.promise;
}
function saveEdge(edge) {
var deferred = $q.defer();
var url = '/api/edge';
$http.post(url, edge).then(function success(response) {
deferred.resolve(response.data);
}, function fail(response) {
deferred.reject(response.data);
});
return deferred.promise;
}
function deleteEdge(edgeId) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId;
$http.delete(url).then(function success() {
deferred.resolve();
}, function fail(response) {
deferred.reject(response.data);
});
return deferred.promise;
}
function getEdgeTypes(config) {
var deferred = $q.defer();
var url = '/api/edge/types';
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getTenantEdges(pageLink, applyCustomersInfo, config, type) {
var deferred = $q.defer();
var url = '/api/tenant/edges?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
url += '&textSearch=' + pageLink.textSearch;
}
if (angular.isDefined(pageLink.idOffset)) {
url += '&idOffset=' + pageLink.idOffset;
}
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
}
if (angular.isDefined(type) && type.length) {
url += '&type=' + type;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
customerService.applyAssignedCustomersInfo(response.data.data).then(
function success(data) {
response.data.data = data;
deferred.resolve(response.data);
},
function fail() {
deferred.reject();
}
);
} else {
deferred.resolve(response.data);
}
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getCustomerEdges(customerId, pageLink, applyCustomersInfo, config, type) {
var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/edges?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
url += '&textSearch=' + pageLink.textSearch;
}
if (angular.isDefined(pageLink.idOffset)) {
url += '&idOffset=' + pageLink.idOffset;
}
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
}
if (angular.isDefined(type) && type.length) {
url += '&type=' + type;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
function success(data) {
response.data.data = data;
deferred.resolve(response.data);
},
function fail() {
deferred.reject();
}
);
} else {
deferred.resolve(response.data);
}
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function assignEdgeToCustomer(customerId, edgeId) {
var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/edge/' + edgeId;
$http.post(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function unassignEdgeFromCustomer(edgeId) {
var deferred = $q.defer();
var url = '/api/customer/edge/' + edgeId;
$http.delete(url).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function makeEdgePublic(edgeId) {
var deferred = $q.defer();
var url = '/api/customer/public/edge/' + edgeId;
$http.post(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function setRootRuleChain(edgeId, ruleChainId) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/' + ruleChainId + '/root';
$http.post(url).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getEdgeEvents(edgeId, pageLink) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/events' + '?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) {
url += '&startTime=' + pageLink.startTime;
}
if (angular.isDefined(pageLink.endTime) && pageLink.endTime != null) {
url += '&endTime=' + pageLink.endTime;
}
if (angular.isDefined(pageLink.idOffset) && pageLink.idOffset != null) {
url += '&offset=' + pageLink.idOffset;
}
$http.get(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail(response) {
deferred.reject(response.data);
});
return deferred.promise;
}
function syncEdge(edgeId) {
var deferred = $q.defer();
var url = '/api/edge/sync';
$http.post(url, edgeId).then(function success(response) {
deferred.resolve(response);
}, function fail(response) {
deferred.reject(response.data);
});
return deferred.promise;
}
}

123
ui/src/app/asset/add-assets-to-edge.controller.js

@ -1,123 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AddAssetsToEdgeController(assetService, $mdDialog, $q, edgeId, assets) {
var vm = this;
vm.assets = assets;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.hasData = hasData;
vm.noData = noData;
vm.searchAssetTextUpdated = searchAssetTextUpdated;
vm.toggleAssetSelection = toggleAssetSelection;
vm.theAssets = {
getItemAtIndex: function (index) {
if (index > vm.assets.data.length) {
vm.theAssets.fetchMoreItems_(index);
return null;
}
var item = vm.assets.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.assets.hasNext) {
return vm.assets.data.length + vm.assets.nextPageLink.limit;
} else {
return vm.assets.data.length;
}
},
fetchMoreItems_: function () {
if (vm.assets.hasNext && !vm.assets.pending) {
vm.assets.pending = true;
assetService.getTenantAssets(vm.assets.nextPageLink, false).then(
function success(assets) {
vm.assets.data = vm.assets.data.concat(assets.data);
vm.assets.nextPageLink = assets.nextPageLink;
vm.assets.hasNext = assets.hasNext;
if (vm.assets.hasNext) {
vm.assets.nextPageLink.limit = vm.assets.pageSize;
}
vm.assets.pending = false;
},
function fail() {
vm.assets.hasNext = false;
vm.assets.pending = false;
});
}
}
};
function cancel () {
$mdDialog.cancel();
}
function assign() {
var tasks = [];
for (var assetId in vm.assets.selections) {
tasks.push(assetService.assignAssetToEdge(edgeId, assetId));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData() {
return vm.assets.data.length == 0 && !vm.assets.hasNext;
}
function hasData() {
return vm.assets.data.length > 0;
}
function toggleAssetSelection($event, asset) {
$event.stopPropagation();
var selected = angular.isDefined(asset.selected) && asset.selected;
asset.selected = !selected;
if (asset.selected) {
vm.assets.selections[asset.id.id] = true;
vm.assets.selectedCount++;
} else {
delete vm.assets.selections[asset.id.id];
vm.assets.selectedCount--;
}
}
function searchAssetTextUpdated() {
vm.assets = {
pageSize: vm.assets.pageSize,
data: [],
nextPageLink: {
limit: vm.assets.pageSize,
textSearch: vm.searchText
},
selections: {},
selectedCount: 0,
hasNext: true,
pending: false
};
}
}

77
ui/src/app/asset/add-assets-to-edge.tpl.html

@ -1,77 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'edge.assign-to-edge' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>asset.assign-asset-to-edge</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>asset.assign-asset-to-edge-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="asset-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchAssetTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">asset.no-assets-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="asset in vm.theAssets" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleAssetSelection($event, asset)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="asset.selected"></md-checkbox>
<span> {{ asset.name }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.assets.selectedCount == 0" type="submit"
class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

123
ui/src/app/dashboard/add-dashboards-to-edge.controller.js

@ -1,123 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AddDashboardsToEdgeController(dashboardService, types, $mdDialog, $q, edgeId, dashboards) {
var vm = this;
vm.types = types;
vm.dashboards = dashboards;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.hasData = hasData;
vm.noData = noData;
vm.searchDashboardTextUpdated = searchDashboardTextUpdated;
vm.toggleDashboardSelection = toggleDashboardSelection;
vm.theDashboards = {
getItemAtIndex: function (index) {
if (index > vm.dashboards.data.length) {
vm.theDashboards.fetchMoreItems_(index);
return null;
}
var item = vm.dashboards.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.dashboards.hasNext) {
return vm.dashboards.data.length + vm.dashboards.nextPageLink.limit;
} else {
return vm.dashboards.data.length;
}
},
fetchMoreItems_: function () {
if (vm.dashboards.hasNext && !vm.dashboards.pending) {
vm.dashboards.pending = true;
dashboardService.getTenantDashboards(vm.dashboards.nextPageLink).then(
function success(dashboards) {
vm.dashboards.data = vm.dashboards.data.concat(dashboards.data);
vm.dashboards.nextPageLink = dashboards.nextPageLink;
vm.dashboards.hasNext = dashboards.hasNext;
if (vm.dashboards.hasNext) {
vm.dashboards.nextPageLink.limit = vm.dashboards.pageSize;
}
vm.dashboards.pending = false;
},
function fail() {
vm.dashboards.hasNext = false;
vm.dashboards.pending = false;
});
}
}
}
function cancel () {
$mdDialog.cancel();
}
function assign () {
var tasks = [];
for (var dashboardId in vm.dashboards.selections) {
tasks.push(dashboardService.assignDashboardToEdge(edgeId, dashboardId));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData () {
return vm.dashboards.data.length == 0 && !vm.dashboards.hasNext;
}
function hasData () {
return vm.dashboards.data.length > 0;
}
function toggleDashboardSelection ($event, dashboard) {
$event.stopPropagation();
var selected = angular.isDefined(dashboard.selected) && dashboard.selected;
dashboard.selected = !selected;
if (dashboard.selected) {
vm.dashboards.selections[dashboard.id.id] = true;
vm.dashboards.selectedCount++;
} else {
delete vm.dashboards.selections[dashboard.id.id];
vm.dashboards.selectedCount--;
}
}
function searchDashboardTextUpdated () {
vm.dashboards = {
pageSize: vm.dashboards.pageSize,
data: [],
nextPageLink: {
limit: vm.dashboards.pageSize,
textSearch: vm.searchText
},
selections: {},
selectedCount: 0,
hasNext: true,
pending: false
};
}
}

77
ui/src/app/dashboard/add-dashboards-to-edge.tpl.html

@ -1,77 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'dashboard.assign-dashboard-to-edge' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>dashboard.assign-dashboard-to-edge</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>dashboard.assign-dashboard-to-edge-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="dashboard-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchDashboardTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">dashboard.no-dashboards-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="dashboard in vm.theDashboards" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleDashboardSelection($event, dashboard)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="dashboard.selected"></md-checkbox>
<span> {{ dashboard.title }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.dashboards.selectedCount == 0" type="submit"
class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

123
ui/src/app/device/add-devices-to-edge.controller.js

@ -1,123 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AddDevicesToEdgeController(deviceService, $mdDialog, $q, edgeId, devices) {
var vm = this;
vm.devices = devices;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.hasData = hasData;
vm.noData = noData;
vm.searchDeviceTextUpdated = searchDeviceTextUpdated;
vm.toggleDeviceSelection = toggleDeviceSelection;
vm.theDevices = {
getItemAtIndex: function (index) {
if (index > vm.devices.data.length) {
vm.theDevices.fetchMoreItems_(index);
return null;
}
var item = vm.devices.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.devices.hasNext) {
return vm.devices.data.length + vm.devices.nextPageLink.limit;
} else {
return vm.devices.data.length;
}
},
fetchMoreItems_: function () {
if (vm.devices.hasNext && !vm.devices.pending) {
vm.devices.pending = true;
deviceService.getTenantDevices(vm.devices.nextPageLink, false).then(
function success(devices) {
vm.devices.data = vm.devices.data.concat(devices.data);
vm.devices.nextPageLink = devices.nextPageLink;
vm.devices.hasNext = devices.hasNext;
if (vm.devices.hasNext) {
vm.devices.nextPageLink.limit = vm.devices.pageSize;
}
vm.devices.pending = false;
},
function fail() {
vm.devices.hasNext = false;
vm.devices.pending = false;
});
}
}
};
function cancel () {
$mdDialog.cancel();
}
function assign() {
var tasks = [];
for (var deviceId in vm.devices.selections) {
tasks.push(deviceService.assignDeviceToEdge(edgeId, deviceId));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData() {
return vm.devices.data.length == 0 && !vm.devices.hasNext;
}
function hasData() {
return vm.devices.data.length > 0;
}
function toggleDeviceSelection($event, device) {
$event.stopPropagation();
var selected = angular.isDefined(device.selected) && device.selected;
device.selected = !selected;
if (device.selected) {
vm.devices.selections[device.id.id] = true;
vm.devices.selectedCount++;
} else {
delete vm.devices.selections[device.id.id];
vm.devices.selectedCount--;
}
}
function searchDeviceTextUpdated() {
vm.devices = {
pageSize: vm.devices.pageSize,
data: [],
nextPageLink: {
limit: vm.devices.pageSize,
textSearch: vm.searchText
},
selections: {},
selectedCount: 0,
hasNext: true,
pending: false
};
}
}

77
ui/src/app/device/add-devices-to-edge.tpl.html

@ -1,77 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'edge.assign-to-edge' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>device.assign-device-to-edge</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>device.assign-device-to-edge-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="device-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchDeviceTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">device.no-devices-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="device in vm.theDevices" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleDeviceSelection($event, device)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="device.selected"></md-checkbox>
<span> {{ device.name }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.devices.selectedCount == 0" type="submit"
class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

46
ui/src/app/edge/add-edge.tpl.html

@ -1,46 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'edge.add' | translate }}" tb-help="'edges'" help-container-id="help-container" style="width: 600px;">
<form name="theForm" ng-submit="vm.add()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>edge.add</h2>
<span flex></span>
<div id="help-container"></div>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<tb-edge edge="vm.item" is-edit="true" is-create="true" the-form="theForm"></tb-edge>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.add' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions>
</form>
</md-dialog>

123
ui/src/app/edge/add-edges-to-customer.controller.js

@ -1,123 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AddEdgesToCustomerController(edgeService, $mdDialog, $q, customerId, edges) {
var vm = this;
vm.edges = edges;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.hasData = hasData;
vm.noData = noData;
vm.searchEdgeTextUpdated = searchEdgeTextUpdated;
vm.toggleEdgeSelection = toggleEdgeSelection;
vm.theEdges = {
getItemAtIndex: function (index) {
if (index > vm.edges.data.length) {
vm.theEdges.fetchMoreItems_(index);
return null;
}
var item = vm.edges.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.edges.hasNext) {
return vm.edges.data.length + vm.edges.nextPageLink.limit;
} else {
return vm.edges.data.length;
}
},
fetchMoreItems_: function () {
if (vm.edges.hasNext && !vm.edges.pending) {
vm.edges.pending = true;
edgeService.getTenantEdges(vm.edges.nextPageLink, false).then(
function success(edges) {
vm.edges.data = vm.edges.data.concat(edges.data);
vm.edges.nextPageLink = edges.nextPageLink;
vm.edges.hasNext = edges.hasNext;
if (vm.edges.hasNext) {
vm.edges.nextPageLink.limit = vm.edges.pageSize;
}
vm.edges.pending = false;
},
function fail() {
vm.edges.hasNext = false;
vm.edges.pending = false;
});
}
}
};
function cancel () {
$mdDialog.cancel();
}
function assign() {
var tasks = [];
for (var edgeId in vm.edges.selections) {
tasks.push(edgeService.assignEdgeToCustomer(customerId, edgeId));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData() {
return vm.edges.data.length == 0 && !vm.edges.hasNext;
}
function hasData() {
return vm.edges.data.length > 0;
}
function toggleEdgeSelection($event, edge) {
$event.stopPropagation();
var selected = angular.isDefined(edge.selected) && edge.selected;
edge.selected = !selected;
if (edge.selected) {
vm.edges.selections[edge.id.id] = true;
vm.edges.selectedCount++;
} else {
delete vm.edges.selections[edge.id.id];
vm.edges.selectedCount--;
}
}
function searchEdgeTextUpdated() {
vm.edges = {
pageSize: vm.edges.pageSize,
data: [],
nextPageLink: {
limit: vm.edges.pageSize,
textSearch: vm.searchText
},
selections: {},
selectedCount: 0,
hasNext: true,
pending: false
};
}
}

77
ui/src/app/edge/add-edges-to-customer.tpl.html

@ -1,77 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'edge.assign-to-customer' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>edge.assign-edge-to-customer</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>edge.assign-edge-to-customer-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="edge-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchEdgeTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">edge.no-edges-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="edge in vm.theEdges" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleEdgeSelection($event, edge)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="edge.selected"></md-checkbox>
<span> {{ edge.name }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.edges.selectedCount == 0" type="submit"
class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

123
ui/src/app/edge/assign-to-customer.controller.js

@ -1,123 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AssignEdgeToCustomerController(customerService, edgeService, $mdDialog, $q, edgeIds, customers) {
var vm = this;
vm.customers = customers;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.isCustomerSelected = isCustomerSelected;
vm.hasData = hasData;
vm.noData = noData;
vm.searchCustomerTextUpdated = searchCustomerTextUpdated;
vm.toggleCustomerSelection = toggleCustomerSelection;
vm.theCustomers = {
getItemAtIndex: function (index) {
if (index > vm.customers.data.length) {
vm.theCustomers.fetchMoreItems_(index);
return null;
}
var item = vm.customers.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.customers.hasNext) {
return vm.customers.data.length + vm.customers.nextPageLink.limit;
} else {
return vm.customers.data.length;
}
},
fetchMoreItems_: function () {
if (vm.customers.hasNext && !vm.customers.pending) {
vm.customers.pending = true;
customerService.getCustomers(vm.customers.nextPageLink).then(
function success(customers) {
vm.customers.data = vm.customers.data.concat(customers.data);
vm.customers.nextPageLink = customers.nextPageLink;
vm.customers.hasNext = customers.hasNext;
if (vm.customers.hasNext) {
vm.customers.nextPageLink.limit = vm.customers.pageSize;
}
vm.customers.pending = false;
},
function fail() {
vm.customers.hasNext = false;
vm.customers.pending = false;
});
}
}
};
function cancel() {
$mdDialog.cancel();
}
function assign() {
var tasks = [];
for (var i=0;i<edgeIds.length;i++) {
tasks.push(edgeService.assignEdgeToCustomer(vm.customers.selection.id.id, edgeIds[i]));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData() {
return vm.customers.data.length == 0 && !vm.customers.hasNext;
}
function hasData() {
return vm.customers.data.length > 0;
}
function toggleCustomerSelection($event, customer) {
$event.stopPropagation();
if (vm.isCustomerSelected(customer)) {
vm.customers.selection = null;
} else {
vm.customers.selection = customer;
}
}
function isCustomerSelected(customer) {
return vm.customers.selection != null && customer &&
customer.id.id === vm.customers.selection.id.id;
}
function searchCustomerTextUpdated() {
vm.customers = {
pageSize: vm.customers.pageSize,
data: [],
nextPageLink: {
limit: vm.customers.pageSize,
textSearch: vm.searchText
},
selection: null,
hasNext: true,
pending: false
};
}
}

76
ui/src/app/edge/assign-to-customer.tpl.html

@ -1,76 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'edge.assign-edge-to-customer' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>edge.assign-edge-to-customer</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>edge.assign-to-customer-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="customer-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchCustomerTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">customer.no-customers-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="customer in vm.theCustomers" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleCustomerSelection($event, customer)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="vm.isCustomerSelected(customer)"></md-checkbox>
<span> {{ customer.title }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

23
ui/src/app/edge/edge-card.tpl.html

@ -1,23 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div flex layout="column">
<div style="text-transform: uppercase; padding-bottom: 5px;">{{vm.item.type}}</div>
<div class="tb-card-description">{{vm.item.additionalInfo.description}}</div>
<div style="padding-top: 5px;" class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'edge.assigned-to-customer' | translate}} '{{vm.item.assignedCustomer.title}}'</div>
<div style="padding-top: 5px;" class="tb-small" ng-show="vm.isPublic()">{{'edge.public' | translate}}</div>
</div>

152
ui/src/app/edge/edge-fieldset.tpl.html

@ -1,152 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-button ng-click="onAssignToCustomer({event: $event})"
ng-show="!isEdit && edgeScope === 'tenant' && !isAssignedToCustomer"
class="md-raised md-primary">{{ 'edge.assign-to-customer' | translate }}</md-button>
<md-button ng-click="onUnassignFromCustomer({event: $event, isPublic: isPublic})"
ng-show="!isEdit && (edgeScope === 'customer' || edgeScope === 'tenant') && isAssignedToCustomer"
class="md-raised md-primary">{{ isPublic ? 'edge.make-private' : 'edge.unassign-from-customer' | translate }}</md-button>
<md-button ng-click="onManageEdgeAssets({event: $event})"
ng-show="!isEdit && edgeScope === 'tenant'"
class="md-raised md-primary">{{ 'edge.manage-edge-assets' | translate }}</md-button>
<md-button ng-click="onManageEdgeDevices({event: $event})"
ng-show="!isEdit && edgeScope === 'tenant'"
class="md-raised md-primary">{{ 'edge.manage-edge-devices' | translate }}</md-button>
<md-button ng-click="onManageEdgeEntityViews({event: $event})"
ng-show="!isEdit && edgeScope === 'tenant'"
class="md-raised md-primary">{{ 'edge.manage-edge-entity-views' | translate }}</md-button>
<md-button ng-click="onManageEdgeDashboards({event: $event})"
ng-show="!isEdit && edgeScope === 'tenant'"
class="md-raised md-primary">{{ 'edge.manage-edge-dashboards' | translate }}</md-button>
<md-button ng-click="onManageEdgeRuleChains({event: $event})"
ng-show="!isEdit && edgeScope === 'tenant'"
class="md-raised md-primary">{{ 'edge.manage-edge-rulechains' | translate }}</md-button>
<md-button ng-click="onDeleteEdge({event: $event})"
ng-show="!isEdit && edgeScope === 'tenant'"
class="md-raised md-primary">{{ 'edge.delete' | translate }}</md-button>
<div layout="row">
<md-button ngclipboard data-clipboard-action="copy"
ngclipboard-success="onEdgeIdCopied()"
data-clipboard-text="{{edge.id.id}}" ng-show="!isEdit"
class="md-raised">
<md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon>
<span translate>edge.copy-id</span>
</md-button>
<md-button ngclipboard data-clipboard-action="copy"
ngclipboard-success="onEdgeInfoCopied('key')"
data-clipboard-text="{{edge.routingKey}}" ng-show="!isEdit"
class="md-raised">
<md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon>
<span translate>edge.copy-edge-key</span>
</md-button>
<md-button ngclipboard data-clipboard-action="copy"
ngclipboard-success="onEdgeInfoCopied('secret')"
data-clipboard-text="{{edge.secret}}" ng-show="!isEdit"
class="md-raised">
<md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon>
<span translate>edge.copy-edge-secret</span>
</md-button>
<md-button ng-click="onEdgeSync(edge.id)"
ng-show="!isEdit"
class="md-raised">
<md-icon md-svg-icon="mdi:sync"></md-icon>
<span translate>edge.sync</span>
</md-button>
</div>
<md-content class="md-padding" layout="column">
<md-input-container class="md-block"
ng-show="!isEdit && isAssignedToCustomer && !isPublic && edgeScope === 'tenant'">
<label translate>edge.assigned-to-customer</label>
<input ng-model="assignedCustomer.title" disabled>
</md-input-container>
<div class="tb-small" style="padding-bottom: 10px; padding-left: 2px;"
ng-show="!isEdit && isPublic && (edgeScope === 'customer' || edgeScope === 'tenant')">
{{ 'edge.edge-public' | translate }}
</div>
<fieldset ng-disabled="$root.loading || !isEdit">
<md-input-container class="md-block">
<label translate>edge.name</label>
<input required name="name" ng-model="edge.name">
<div ng-messages="theForm.name.$error">
<div translate ng-message="required">edge.name-required</div>
</div>
</md-input-container>
<tb-entity-subtype-autocomplete
ng-disabled="$root.loading || !isEdit"
tb-required="true"
the-form="theForm"
ng-model="edge.type"
entity-type="types.entityType.edge">
</tb-entity-subtype-autocomplete>
<md-input-container class="md-block">
<label translate>edge.label</label>
<input name="label" ng-model="edge.label">
</md-input-container>
<md-input-container class="md-block">
<label translate>edge.description</label>
<textarea ng-model="edge.additionalInfo.description" rows="2"></textarea>
</md-input-container>
<md-input-container class="md-block">
<label translate>edge.edge-license-key</label>
<input required name="edgeLicenseKey" ng-model="edge.edgeLicenseKey">
<div ng-messages="theForm.edgeLicenseKey.$error">
<div translate ng-message="required">edge.edge-license-key-required</div>
</div>
</md-input-container>
<md-input-container class="md-block">
<label translate>edge.cloud-endpoint</label>
<input required name="cloudEndpoint" ng-model="edge.cloudEndpoint">
<div ng-messages="theForm.cloudEndpoint.$error">
<div translate ng-message="required">edge.cloud-endpoint-required</div>
</div>
</md-input-container>
</fieldset>
<div layout="row">
<md-input-container class="md-block" flex>
<label translate>edge.edge-key</label>
<input ng-model="edge.routingKey" disabled>
</md-input-container>
<md-button class="md-icon-button" style="margin-top: 14px;"
ngclipboard data-clipboard-action="copy"
ngclipboard-success="onEdgeInfoCopied('key')"
data-clipboard-text="{{edge.routingKey}}">
<md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon>
<md-tooltip md-direction="top">
{{ 'edge.copy-edge-key' | translate }}
</md-tooltip>
</md-button>
</div>
<div layout="row">
<md-input-container class="md-block" flex>
<label translate>edge.edge-secret</label>
<input ng-model="edge.secret" disabled>
</md-input-container>
<md-button class="md-icon-button" style="margin-top: 14px;"
ngclipboard data-clipboard-action="copy"
data-clipboard-text="{{edge.secret}}"
ngclipboard-success="onEdgeInfoCopied('secret')">
<md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon>
<md-tooltip md-direction="top">
{{ 'edge.copy-edge-secret' | translate }}
</md-tooltip>
</md-button>
</div>
</md-content>

695
ui/src/app/edge/edge.controller.js

@ -1,695 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable import/no-unresolved, import/default */
import addEdgeTemplate from './add-edge.tpl.html';
import edgeCard from './edge-card.tpl.html';
import assignToCustomerTemplate from './assign-to-customer.tpl.html';
import addEdgesToCustomerTemplate from './add-edges-to-customer.tpl.html';
import setRootRuleChainToEdgesTemplate from './set-root-rulechain-to-edges.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export function EdgeCardController(types) {
var vm = this;
vm.types = types;
vm.isAssignedToCustomer = function() {
if (vm.item && vm.item.customerId && vm.parentCtl.edgesScope === 'tenant' &&
vm.item.customerId.id != vm.types.id.nullUid && !vm.item.assignedCustomer.isPublic) {
return true;
}
return false;
}
vm.isPublic = function() {
if (vm.item && vm.item.assignedCustomer && vm.parentCtl.edgesScope === 'tenant' && vm.item.assignedCustomer.isPublic) {
return true;
}
return false;
}
}
/*@ngInject*/
export function EdgeController($rootScope, userService, edgeService, customerService, ruleChainService,
$state, $stateParams, $document, $mdDialog, $q, $translate, types, importExport) {
var customerId = $stateParams.customerId;
var edgeActionsList = [];
var edgeGroupActionsList = [];
var edgeAddItemActionsList = [
{
onAction: function ($event) {
vm.grid.addItem($event);
},
name: function() { return $translate.instant('action.add') },
details: function() { return $translate.instant('edge.add-edge-text') },
icon: "insert_drive_file"
},
{
onAction: function ($event) {
importExport.importEntities($event, types.entityType.edge).then(
function() {
vm.grid.refreshList();
}
);
},
name: function() { return $translate.instant('action.import') },
details: function() { return $translate.instant('edge.import') },
icon: "file_upload"
}
];
var vm = this;
vm.types = types;
vm.edgeGridConfig = {
deleteItemTitleFunc: deleteEdgeTitle,
deleteItemContentFunc: deleteEdgeText,
deleteItemsTitleFunc: deleteEdgesTitle,
deleteItemsActionTitleFunc: deleteEdgesActionTitle,
deleteItemsContentFunc: deleteEdgesText,
saveItemFunc: saveEdge,
getItemTitleFunc: getEdgeTitle,
itemCardController: 'EdgeCardController',
itemCardTemplateUrl: edgeCard,
parentCtl: vm,
actionsList: edgeActionsList,
groupActionsList: edgeGroupActionsList,
addItemActions: edgeAddItemActionsList,
onGridInited: gridInited,
addItemTemplateUrl: addEdgeTemplate,
addItemText: function() { return $translate.instant('edge.add-edge-text') },
noItemsText: function() { return $translate.instant('edge.no-edges-text') },
itemDetailsText: function() { return $translate.instant('edge.edge-details') },
isDetailsReadOnly: isCustomerUser,
isSelectionEnabled: function () {
return !isCustomerUser();
}
};
if (angular.isDefined($stateParams.items) && $stateParams.items !== null) {
vm.edgeGridConfig.items = $stateParams.items;
}
if (angular.isDefined($stateParams.topIndex) && $stateParams.topIndex > 0) {
vm.edgeGridConfig.topIndex = $stateParams.topIndex;
}
vm.edgesScope = $state.$current.data.edgesType;
vm.assignToCustomer = assignToCustomer;
vm.makePublic = makePublic;
vm.unassignFromCustomer = unassignFromCustomer;
vm.openEdgeAssets = openEdgeAssets;
vm.openEdgeDevices = openEdgeDevices;
vm.openEdgeEntityViews = openEdgeEntityViews;
vm.openEdgeDashboards = openEdgeDashboards;
vm.openEdgeRuleChains = openEdgeRuleChains;
initController();
function initController() {
var fetchEdgesFunction = null;
var deleteEdgeFunction = null;
var refreshEdgesParamsFunction = null;
var user = userService.getCurrentUser();
if (user.authority === 'CUSTOMER_USER') {
vm.edgesScope = 'customer_user';
customerId = user.customerId;
}
if (customerId) {
vm.customerEdgesTitle = $translate.instant('customer.edges');
customerService.getShortCustomerInfo(customerId).then(
function success(info) {
if (info.isPublic) {
vm.customerEdgesTitle = $translate.instant('customer.public-edges');
}
}
);
}
if (vm.edgesScope === 'tenant') {
fetchEdgesFunction = function (pageLink, edgeType) {
return edgeService.getTenantEdges(pageLink, true, null, edgeType);
};
deleteEdgeFunction = function (edgeId) {
return edgeService.deleteEdge(edgeId);
};
refreshEdgesParamsFunction = function() {
return {"topIndex": vm.topIndex};
};
edgeActionsList.push({
onAction: function ($event, item) {
makePublic($event, item);
},
name: function() { return $translate.instant('action.share') },
details: function() { return $translate.instant('edge.make-public') },
icon: "share",
isEnabled: function(edge) {
return edge && (!edge.customerId || edge.customerId.id === types.id.nullUid);
}
});
edgeActionsList.push(
{
onAction: function ($event, item) {
assignToCustomer($event, [ item.id.id ]);
},
name: function() { return $translate.instant('action.assign') },
details: function() { return $translate.instant('edge.assign-to-customer') },
icon: "assignment_ind",
isEnabled: function(edge) {
return edge && (!edge.customerId || edge.customerId.id === types.id.nullUid);
}
}
);
edgeActionsList.push(
{
onAction: function ($event, item) {
unassignFromCustomer($event, item, false);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('edge.unassign-from-customer') },
icon: "assignment_return",
isEnabled: function(edge) {
return edge && edge.customerId && edge.customerId.id !== types.id.nullUid && !edge.assignedCustomer.isPublic;
}
}
);
edgeActionsList.push({
onAction: function ($event, item) {
unassignFromCustomer($event, item, true);
},
name: function() { return $translate.instant('action.make-private') },
details: function() { return $translate.instant('edge.make-private') },
icon: "reply",
isEnabled: function(edge) {
return edge && edge.customerId && edge.customerId.id !== types.id.nullUid && edge.assignedCustomer.isPublic;
}
});
edgeActionsList.push(
{
onAction: function ($event, item) {
openEdgeAssets($event, item);
},
name: function() { return $translate.instant('asset.assets') },
details: function() {
return $translate.instant('edge.manage-edge-assets');
},
icon: "domain"
}
);
edgeActionsList.push(
{
onAction: function ($event, item) {
openEdgeDevices($event, item);
},
name: function() { return $translate.instant('device.devices') },
details: function() {
return $translate.instant('edge.manage-edge-devices');
},
icon: "devices_other"
}
);
edgeActionsList.push(
{
onAction: function ($event, item) {
openEdgeEntityViews($event, item);
},
name: function() { return $translate.instant('entity-view.entity-views') },
details: function() {
return $translate.instant('edge.manage-edge-entity-views');
},
icon: "view_quilt"
}
);
edgeActionsList.push(
{
onAction: function ($event, item) {
openEdgeDashboards($event, item);
},
name: function() { return $translate.instant('dashboard.dashboards') },
details: function() {
return $translate.instant('edge.manage-edge-dashboards');
},
icon: "dashboard"
}
);
edgeActionsList.push(
{
onAction: function ($event, item) {
openEdgeRuleChains($event, item);
},
name: function() { return $translate.instant('rulechain.rulechains') },
details: function() {
return $translate.instant('edge.manage-edge-rulechains');
},
icon: "code"
}
);
edgeActionsList.push(
{
onAction: function ($event, item) {
vm.grid.deleteItem($event, item);
},
name: function() { return $translate.instant('action.delete') },
details: function() { return $translate.instant('edge.delete') },
icon: "delete"
}
);
edgeGroupActionsList.push(
{
onAction: function ($event, items) {
setRootRuleChainToEdges($event, items);
},
name: function() { return $translate.instant('edge.set-rootrule-chain-to-edges') },
details: function(selectedCount) {
return $translate.instant('edge.set-root-rulechain-to-edges-text', {count: selectedCount}, "messageformat");
},
icon: "flag"
}
);
edgeGroupActionsList.push(
{
onAction: function ($event, items) {
assignEdgesToCustomer($event, items);
},
name: function() { return $translate.instant('edge.assign-edges') },
details: function(selectedCount) {
return $translate.instant('edge.assign-edges-text', {count: selectedCount}, "messageformat");
},
icon: "assignment_ind"
}
);
edgeGroupActionsList.push(
{
onAction: function ($event) {
vm.grid.deleteItems($event);
},
name: function() { return $translate.instant('edge.delete-edges') },
details: deleteEdgesActionTitle,
icon: "delete"
}
);
} else if (vm.edgesScope === 'customer' || vm.edgesScope === 'customer_user') {
fetchEdgesFunction = function (pageLink, edgeType) {
return edgeService.getCustomerEdges(customerId, pageLink, true, null, edgeType);
};
deleteEdgeFunction = function (edgeId) {
return edgeService.unassignEdgeFromCustomer(edgeId);
};
refreshEdgesParamsFunction = function () {
return {"customerId": customerId, "topIndex": vm.topIndex};
};
if (vm.edgesScope === 'customer') {
edgeActionsList.push(
{
onAction: function ($event, item) {
unassignFromCustomer($event, item, false);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('edge.unassign-from-customer') },
icon: "assignment_return",
isEnabled: function(edge) {
return edge && !edge.assignedCustomer.isPublic;
}
}
);
edgeActionsList.push(
{
onAction: function ($event, item) {
unassignFromCustomer($event, item, true);
},
name: function() { return $translate.instant('action.make-private') },
details: function() { return $translate.instant('edge.make-private') },
icon: "reply",
isEnabled: function(edge) {
return edge && edge.assignedCustomer.isPublic;
}
}
);
edgeGroupActionsList.push(
{
onAction: function ($event, items) {
unassignEdgesFromCustomer($event, items);
},
name: function() { return $translate.instant('edge.unassign-edges') },
details: function(selectedCount) {
return $translate.instant('edge.unassign-edges-action-title', {count: selectedCount}, "messageformat");
},
icon: "assignment_return"
}
);
vm.edgeGridConfig.addItemAction = {
onAction: function ($event) {
addEdgesToCustomer($event);
},
name: function() { return $translate.instant('edge.assign-edges') },
details: function() { return $translate.instant('edge.assign-new-edge') },
icon: "add"
};
} else if (vm.edgesScope === 'customer_user') {
vm.edgeGridConfig.addItemAction = {};
}
vm.edgeGridConfig.addItemActions = [];
}
vm.edgeGridConfig.refreshParamsFunc = refreshEdgesParamsFunction;
vm.edgeGridConfig.fetchItemsFunc = fetchEdgesFunction;
vm.edgeGridConfig.deleteItemFunc = deleteEdgeFunction;
}
function deleteEdgeTitle(edge) {
return $translate.instant('edge.delete-edge-title', {edgeName: edge.name});
}
function deleteEdgeText() {
return $translate.instant('edge.delete-edge-text');
}
function deleteEdgesTitle(selectedCount) {
return $translate.instant('edge.delete-edges-title', {count: selectedCount}, 'messageformat');
}
function deleteEdgesActionTitle(selectedCount) {
return $translate.instant('edge.delete-edges-action-title', {count: selectedCount}, 'messageformat');
}
function deleteEdgesText () {
return $translate.instant('edge.delete-edges-text');
}
function gridInited(grid) {
vm.grid = grid;
}
function getEdgeTitle(edge) {
return edge ? edge.name : '';
}
function saveEdge(edge) {
var deferred = $q.defer();
edgeService.saveEdge(edge).then(
function success(savedEdge) {
$rootScope.$broadcast('edgeSaved');
var edges = [ savedEdge ];
customerService.applyAssignedCustomersInfo(edges).then(
function success(items) {
if (items && items.length == 1) {
deferred.resolve(items[0]);
} else {
deferred.reject();
}
},
function fail() {
deferred.reject();
}
);
},
function fail() {
deferred.reject();
}
);
return deferred.promise;
}
function isCustomerUser() {
return vm.edgesScope === 'customer_user';
}
function assignToCustomer($event, edgeIds) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
customerService.getCustomers({limit: pageSize, textSearch: ''}).then(
function success(_customers) {
var customers = {
pageSize: pageSize,
data: _customers.data,
nextPageLink: _customers.nextPageLink,
selection: null,
hasNext: _customers.hasNext,
pending: false
};
if (customers.hasNext) {
customers.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AssignEdgeToCustomerController',
controllerAs: 'vm',
templateUrl: assignToCustomerTemplate,
locals: {edgeIds: edgeIds, customers: customers},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function addEdgesToCustomer($event) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
edgeService.getTenantEdges({limit: pageSize, textSearch: ''}, false).then(
function success(_edges) {
var edges = {
pageSize: pageSize,
data: _edges.data,
nextPageLink: _edges.nextPageLink,
selections: {},
selectedCount: 0,
hasNext: _edges.hasNext,
pending: false
};
if (edges.hasNext) {
edges.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AddEdgesToCustomerController',
controllerAs: 'vm',
templateUrl: addEdgesToCustomerTemplate,
locals: {customerId: customerId, edges: edges},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function setRootRuleChainToEdges($event, items) {
var edgeIds = [];
for (var id in items.selections) {
edgeIds.push(id);
}
setRootRuleChain($event, edgeIds);
}
function setRootRuleChain($event, edgeIds) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
ruleChainService.getEdgesRuleChains({limit: pageSize, textSearch: ''}).then(
function success(_ruleChains) {
var ruleChains = {
pageSize: pageSize,
data: _ruleChains.data,
nextPageLink: _ruleChains.nextPageLink,
selection: null,
hasNext: _ruleChains.hasNext,
pending: false
};
if (ruleChains.hasNext) {
ruleChains.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'SetRootRuleChainToEdgesController',
controllerAs: 'vm',
templateUrl: setRootRuleChainToEdgesTemplate,
locals: {edgeIds: edgeIds, ruleChains: ruleChains},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function assignEdgesToCustomer($event, items) {
var edgeIds = [];
for (var id in items.selections) {
edgeIds.push(id);
}
assignToCustomer($event, edgeIds);
}
function unassignFromCustomer($event, edge, isPublic) {
if ($event) {
$event.stopPropagation();
}
var title;
var content;
var label;
if (isPublic) {
title = $translate.instant('edge.make-private-edge-title', {edgeName: edge.name});
content = $translate.instant('edge.make-private-edge-text');
label = $translate.instant('edge.make-private');
} else {
title = $translate.instant('edge.unassign-edge-title', {edgeName: edge.name});
content = $translate.instant('edge.unassign-edge-text');
label = $translate.instant('edge.unassign-edge');
}
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title(title)
.htmlContent(content)
.ariaLabel(label)
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
edgeService.unassignEdgeFromCustomer(edge.id.id).then(function success() {
vm.grid.refreshList();
});
});
}
function unassignEdgesFromCustomer($event, items) {
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title($translate.instant('edge.unassign-edges-title', {count: items.selectedCount}, 'messageformat'))
.htmlContent($translate.instant('edge.unassign-edges-text'))
.ariaLabel($translate.instant('edge.unassign-edge'))
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
var tasks = [];
for (var id in items.selections) {
tasks.push(edgeService.unassignEdgeFromCustomer(id));
}
$q.all(tasks).then(function () {
vm.grid.refreshList();
});
});
}
function makePublic($event, edge) {
if ($event) {
$event.stopPropagation();
}
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title($translate.instant('edge.make-public-edge-title', {edgeName: edge.name}))
.htmlContent($translate.instant('edge.make-public-edge-text'))
.ariaLabel($translate.instant('edge.make-public'))
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
edgeService.makeEdgePublic(edge.id.id).then(function success() {
vm.grid.refreshList();
});
});
}
function openEdgeDashboards($event, edge) {
if ($event) {
$event.stopPropagation();
}
$state.go('home.edges.dashboards', {edgeId: edge.id.id});
}
function openEdgeRuleChains($event, edge) {
if ($event) {
$event.stopPropagation();
}
$state.go('home.edges.ruleChains', {edgeId: edge.id.id});
}
function openEdgeAssets($event, edge) {
if ($event) {
$event.stopPropagation();
}
$state.go('home.edges.assets', {edgeId: edge.id.id});
}
function openEdgeDevices($event, edge) {
if ($event) {
$event.stopPropagation();
}
$state.go('home.edges.devices', {edgeId: edge.id.id});
}
function openEdgeEntityViews($event, edge) {
if ($event) {
$event.stopPropagation();
}
$state.go('home.edges.entityViews', {edgeId: edge.id.id});
}
}

118
ui/src/app/edge/edge.directive.js

@ -1,118 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable import/no-unresolved, import/default */
import edgeFieldsetTemplate from './edge-fieldset.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, utils, toast, types, customerService, edgeService) {
var linker = function (scope, element) {
var template = $templateCache.get(edgeFieldsetTemplate);
element.html(template);
scope.types = types;
scope.isAssignedToCustomer = false;
scope.isPublic = false;
scope.assignedCustomer = null;
scope.$watch('edge', function(newVal) {
if (newVal) {
if (!scope.edge.id) {
scope.edge.routingKey = utils.guid('');
scope.edge.secret = generateSecret(20);
scope.edge.cloudEndpoint = utils.baseUrl();
}
if (scope.edge.customerId && scope.edge.customerId.id !== types.id.nullUid) {
scope.isAssignedToCustomer = true;
customerService.getShortCustomerInfo(scope.edge.customerId.id).then(
function success(customer) {
scope.assignedCustomer = customer;
scope.isPublic = customer.isPublic;
}
);
} else {
scope.isAssignedToCustomer = false;
scope.isPublic = false;
scope.assignedCustomer = null;
}
}
});
function generateSecret(length) {
if (angular.isUndefined(length) || length == null) {
length = 1;
}
var l = length > 10 ? 10 : length;
var str = Math.random().toString(36).substr(2, l);
if(str.length >= length){
return str;
}
return str.concat(generateSecret(length - str.length));
}
scope.onEdgeIdCopied = function() {
toast.showSuccess($translate.instant('edge.id-copied-message'), 750, angular.element(element).parent().parent(), 'bottom left');
};
scope.onEdgeSync = function (edgeId) {
edgeService.syncEdge(edgeId).then(
function success() {
toast.showSuccess($translate.instant('edge.sync-message'), 750, angular.element(element).parent().parent(), 'bottom left');
},
function fail(error) {
toast.showError(error);
}
);
}
$compile(element.contents())(scope);
scope.onEdgeInfoCopied = function(type) {
let infoTypeLabel = "";
switch (type) {
case 'key':
infoTypeLabel = "edge.edge-key-copied-message";
break;
case 'secret':
infoTypeLabel = "edge.edge-secret-copied-message";
break;
}
toast.showSuccess($translate.instant(infoTypeLabel), 750, angular.element(element).parent().parent(), 'bottom left');
};
};
return {
restrict: "E",
link: linker,
scope: {
edge: '=',
isEdit: '=',
edgeScope: '=',
theForm: '=',
onAssignToCustomer: '&',
onMakePublic: '&',
onUnassignFromCustomer: '&',
onManageEdgeAssets: '&',
onManageEdgeDevices: '&',
onManageEdgeEntityViews: '&',
onManageEdgeDashboards: '&',
onManageEdgeRuleChains: '&',
onDeleteEdge: '&'
}
};
}

241
ui/src/app/edge/edge.routes.js

@ -1,241 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable import/no-unresolved, import/default */
import edgesTemplate from './edges.tpl.html';
import entityViewsTemplate from "../entity-view/entity-views.tpl.html";
import devicesTemplate from "../device/devices.tpl.html";
import assetsTemplate from "../asset/assets.tpl.html";
import dashboardsTemplate from "../dashboard/dashboards.tpl.html";
import dashboardTemplate from "../dashboard/dashboard.tpl.html";
import ruleChainsTemplate from "../rulechain/rulechains.tpl.html";
import ruleChainTemplate from "../rulechain/rulechain.tpl.html";
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function EdgeRoutes($stateProvider, types) {
$stateProvider
.state('home.edges', {
url: '/edges',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN', 'CUSTOMER_USER'],
views: {
"content@home": {
templateUrl: edgesTemplate,
controller: 'EdgeController',
controllerAs: 'vm'
}
},
data: {
edgesType: 'tenant',
searchEnabled: true,
searchByEntitySubtype: true,
searchEntityType: types.entityType.edge,
pageTitle: 'edge.edges'
},
ncyBreadcrumb: {
label: '{"icon": "router", "label": "edge.edges"}'
}
}).state('home.edges.entityViews', {
url: '/:edgeId/entityViews',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: entityViewsTemplate,
controllerAs: 'vm',
controller: 'EntityViewController'
}
},
data: {
entityViewsType: 'edge',
searchEnabled: true,
searchByEntitySubtype: true,
searchEntityType: types.entityType.entityView,
pageTitle: 'edge.entity-views'
},
ncyBreadcrumb: {
label: '{"icon": "view_quilt", "label": "edge.entity-views"}'
}
}).state('home.edges.devices', {
url: '/:edgeId/devices',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: devicesTemplate,
controllerAs: 'vm',
controller: 'DeviceController'
}
},
data: {
devicesType: 'edge',
searchEnabled: true,
searchByEntitySubtype: true,
searchEntityType: types.entityType.device,
pageTitle: 'edge.devices'
},
ncyBreadcrumb: {
label: '{"icon": "devices_other", "label": "edge.devices"}'
}
}).state('home.edges.assets', {
url: '/:edgeId/assets',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: assetsTemplate,
controllerAs: 'vm',
controller: 'AssetController'
}
},
data: {
assetsType: 'edge',
searchEnabled: true,
searchByEntitySubtype: true,
searchEntityType: types.entityType.asset,
pageTitle: 'edge.assets'
},
ncyBreadcrumb: {
label: '{"icon": "domain", "label": "edge.assets"}'
}
}).state('home.edges.dashboards', {
url: '/:edgeId/dashboards',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: dashboardsTemplate,
controllerAs: 'vm',
controller: 'DashboardsController'
}
},
data: {
dashboardsType: 'edge',
searchEnabled: true,
pageTitle: 'edge.dashboards'
},
ncyBreadcrumb: {
label: '{"icon": "dashboard", "label": "edge.dashboards"}'
}
}).state('home.edges.dashboards.dashboard', {
url: '/:dashboardId?state',
reloadOnSearch: false,
module: 'private',
auth: ['TENANT_ADMIN', 'CUSTOMER_USER'],
views: {
"content@home": {
templateUrl: dashboardTemplate,
controller: 'DashboardController',
controllerAs: 'vm'
}
},
data: {
widgetEditMode: false,
searchEnabled: false,
pageTitle: 'dashboard.dashboard',
dashboardsType: 'edge',
},
ncyBreadcrumb: {
label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}'
}
}).state('home.customers.edges', {
url: '/:customerId/edges',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: edgesTemplate,
controllerAs: 'vm',
controller: 'EdgeController'
}
},
data: {
edgesType: 'customer',
searchEnabled: true,
searchByEntitySubtype: true,
searchEntityType: types.entityType.edge,
pageTitle: 'customer.edges'
},
ncyBreadcrumb: {
label: '{"icon": "router", "label": "{{ vm.customerEdgesTitle }}", "translate": "false"}'
}
}).state('home.edges.ruleChains', {
url: '/:edgeId/ruleChains',
params: {'topIndex': 0},
module: 'private',
auth: ['TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: ruleChainsTemplate,
controllerAs: 'vm',
controller: 'RuleChainsController'
}
},
data: {
searchEnabled: true,
pageTitle: 'rulechain.edge-rulechains',
ruleChainsType: 'edge'
},
ncyBreadcrumb: {
label: '{"icon": "code", "label": "rulechain.edge-rulechains"}'
}
}).state('home.edges.ruleChains.ruleChain', {
url: '/:ruleChainId',
reloadOnSearch: false,
module: 'private',
auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
views: {
"content@home": {
templateUrl: ruleChainTemplate,
controller: 'RuleChainController',
controllerAs: 'vm'
}
},
resolve: {
ruleChain:
/*@ngInject*/
function($stateParams, ruleChainService) {
return ruleChainService.getRuleChain($stateParams.ruleChainId);
},
ruleChainMetaData:
/*@ngInject*/
function($stateParams, ruleChainService) {
return ruleChainService.getRuleChainMetaData($stateParams.ruleChainId);
},
ruleNodeComponents:
/*@ngInject*/
function($stateParams, ruleChainService) {
return ruleChainService.getRuleNodeComponents(types.ruleChainType.edge);
}
},
data: {
import: false,
searchEnabled: false,
pageTitle: 'edge.rulechain'
},
ncyBreadcrumb: {
label: '{"icon": "code", "label": "{{ vm.ruleChain.name }}", "translate": "false"}'
}
});
}

82
ui/src/app/edge/edges.tpl.html

@ -1,82 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<tb-grid grid-configuration="vm.edgeGridConfig">
<details-buttons tb-help="'edges'" help-container-id="help-container">
<div id="help-container"></div>
</details-buttons>
<md-tabs ng-class="{'tb-headless': vm.grid.detailsConfig.isDetailsEditMode}"
id="tabs" md-border-bottom flex class="tb-absolute-fill">
<md-tab label="{{ 'edge.details' | translate }}">
<tb-edge edge="vm.grid.operatingItem()"
is-edit="vm.grid.detailsConfig.isDetailsEditMode"
edge-scope="vm.edgesScope"
the-form="vm.grid.detailsForm"
on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])"
on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)"
on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, isPublic)"
on-manage-edge-assets="vm.openEdgeAssets(event, vm.grid.detailsConfig.currentItem)"
on-manage-edge-devices="vm.openEdgeDevices(event, vm.grid.detailsConfig.currentItem)"
on-manage-edge-entity-views="vm.openEdgeEntityViews(event, vm.grid.detailsConfig.currentItem)"
on-manage-edge-dashboards="vm.openEdgeDashboards(event, vm.grid.detailsConfig.currentItem)"
on-manage-edge-rule-chains="vm.openEdgeRuleChains(event, vm.grid.detailsConfig.currentItem)"
on-delete-edge="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-edge>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.attributes' | translate }}">
<tb-attribute-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.edge}}"
entity-name="vm.grid.operatingItem().name"
default-attribute-scope="{{vm.types.attributesScope.server.value}}">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.latest-telemetry' | translate }}">
<tb-attribute-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.edge}}"
entity-name="vm.grid.operatingItem().name"
default-attribute-scope="{{vm.types.latestTelemetry.value}}"
disable-attribute-scope-selection="true">
</tb-attribute-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.edge"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'edge.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.edge"
entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.error.value}}">
</tb-event-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}">
<tb-relation-table flex
readonly="!('edge' | hasGenericPermission:'write')"
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.edge}}">
</tb-relation-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.isTenantAdmin()" md-on-select="vm.grid.triggerResize()" label="{{ 'audit-log.audit-logs' | translate }}">
<tb-audit-log-table flex entity-type="vm.types.entityType.edge"
entity-id="vm.grid.operatingItem().id.id"
audit-log-mode="{{vm.types.auditLogMode.entity}}">
</tb-audit-log-table>
</md-tab>
</md-tabs>
</tb-grid>

43
ui/src/app/edge/index.js

@ -1,43 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive';
import thingsboardApiUser from '../api/user.service';
import thingsboardApiEdge from '../api/edge.service';
import thingsboardApiCustomer from '../api/customer.service';
import EdgeRoutes from './edge.routes';
import {EdgeController, EdgeCardController} from './edge.controller';
import AssignEdgeToCustomerController from './assign-to-customer.controller';
import AddEdgesToCustomerController from './add-edges-to-customer.controller';
import SetRootRuleChainToEdgesController from './set-root-rule-chain-to-edges.controller';
import EdgeDirective from './edge.directive';
export default angular.module('thingsboard.edge', [
uiRouter,
thingsboardGrid,
thingsboardApiUser,
thingsboardApiEdge,
thingsboardApiCustomer
])
.config(EdgeRoutes)
.controller('EdgeController', EdgeController)
.controller('EdgeCardController', EdgeCardController)
.controller('AssignEdgeToCustomerController', AssignEdgeToCustomerController)
.controller('AddEdgesToCustomerController', AddEdgesToCustomerController)
.controller('SetRootRuleChainToEdgesController', SetRootRuleChainToEdgesController)
.directive('tbEdge', EdgeDirective)
.name;

129
ui/src/app/edge/set-root-rule-chain-to-edges.controller.js

@ -1,129 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function SetRootRuleChainToEdgesController(ruleChainService, edgeService, $mdDialog, $q, edgeIds, ruleChains) {
var vm = this;
vm.ruleChains = ruleChains;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.isRuleChainSelected = isRuleChainSelected;
vm.hasData = hasData;
vm.noData = noData;
vm.searchRuleChainTextUpdated = searchRuleChainTextUpdated;
vm.toggleRuleChainSelection = toggleRuleChainSelection;
vm.theRuleChains = {
getItemAtIndex: function (index) {
if (index > vm.ruleChains.data.length) {
vm.theRuleChains.fetchMoreItems_(index);
return null;
}
var item = vm.ruleChains.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.ruleChains.hasNext) {
return vm.ruleChains.data.length + vm.ruleChains.nextPageLink.limit;
} else {
return vm.ruleChains.data.length;
}
},
fetchMoreItems_: function () {
if (vm.ruleChains.hasNext && !vm.ruleChains.pending) {
vm.ruleChains.pending = true;
ruleChainService.getEdgesRuleChains(vm.ruleChains.nextPageLink).then(
function success(ruleChains) {
vm.ruleChains.data = vm.ruleChains.data.concat(ruleChains.data);
vm.ruleChains.nextPageLink = ruleChains.nextPageLink;
vm.ruleChains.hasNext = ruleChains.hasNext;
if (vm.ruleChains.hasNext) {
vm.ruleChains.nextPageLink.limit = vm.ruleChains.pageSize;
}
vm.ruleChains.pending = false;
},
function fail() {
vm.ruleChains.hasNext = false;
vm.ruleChains.pending = false;
});
}
}
};
function cancel() {
$mdDialog.cancel();
}
function assign() {
var assignTasks = [];
for (var i=0;i<edgeIds.length;i++) {
assignTasks.push(ruleChainService.assignRuleChainToEdge(edgeIds[i], vm.ruleChains.selection.id.id));
}
$q.all(assignTasks).then(function () {
var setRootTasks = [];
for (var j=0;j<edgeIds.length;j++) {
setRootTasks.push(edgeService.setRootRuleChain(edgeIds[j], vm.ruleChains.selection.id.id));
}
$q.all(setRootTasks).then(function () {
$mdDialog.hide();
});
});
}
function noData() {
return vm.ruleChains.data.length == 0 && !vm.ruleChains.hasNext;
}
function hasData() {
return vm.ruleChains.data.length > 0;
}
function toggleRuleChainSelection($event, ruleChain) {
$event.stopPropagation();
if (vm.isRuleChainSelected(ruleChain)) {
vm.ruleChains.selection = null;
} else {
vm.ruleChains.selection = ruleChain;
}
}
function isRuleChainSelected(ruleChain) {
return vm.ruleChains.selection != null && ruleChain &&
ruleChain.id.id === vm.ruleChains.selection.id.id;
}
function searchRuleChainTextUpdated() {
vm.ruleChains = {
pageSize: vm.ruleChains.pageSize,
data: [],
nextPageLink: {
limit: vm.ruleChains.pageSize,
textSearch: vm.searchText
},
selection: null,
hasNext: true,
pending: false
};
}
}

76
ui/src/app/edge/set-root-rule-chain-to-edges.tpl.html

@ -1,76 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'edge.set-root-rulechain-to-edges' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>edge.set-root-rulechain-to-edges</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>edge.set-root-rulechain-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="rule-chain-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchRuleChainTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">rulechain.no-rulechains-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="ruleChain in vm.theRuleChains" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleRuleChainSelection($event, ruleChain)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="vm.isRuleChainSelected(ruleChain)"></md-checkbox>
<span> {{ ruleChain.name }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.ruleChains.selection==null" type="submit" class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

123
ui/src/app/entity-view/add-entity-views-to-edge.controller.js

@ -1,123 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AddEntityViewsToEdgeController(entityViewService, $mdDialog, $q, edgeId, entityViews) {
var vm = this;
vm.entityViews = entityViews;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.hasData = hasData;
vm.noData = noData;
vm.searchEntityViewTextUpdated = searchEntityViewTextUpdated;
vm.toggleEntityViewSelection = toggleEntityViewSelection;
vm.theEntityViews = {
getItemAtIndex: function (index) {
if (index > vm.entityViews.data.length) {
vm.theEntityViews.fetchMoreItems_(index);
return null;
}
var item = vm.entityViews.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.entityViews.hasNext) {
return vm.entityViews.data.length + vm.entityViews.nextPageLink.limit;
} else {
return vm.entityViews.data.length;
}
},
fetchMoreItems_: function () {
if (vm.entityViews.hasNext && !vm.entityViews.pending) {
vm.entityViews.pending = true;
entityViewService.getTenantEntityViews(vm.entityViews.nextPageLink, false).then(
function success(entityViews) {
vm.entityViews.data = vm.entityViews.data.concat(entityViews.data);
vm.entityViews.nextPageLink = entityViews.nextPageLink;
vm.entityViews.hasNext = entityViews.hasNext;
if (vm.entityViews.hasNext) {
vm.entityViews.nextPageLink.limit = vm.entityViews.pageSize;
}
vm.entityViews.pending = false;
},
function fail() {
vm.entityViews.hasNext = false;
vm.entityViews.pending = false;
});
}
}
};
function cancel () {
$mdDialog.cancel();
}
function assign() {
var tasks = [];
for (var entityViewId in vm.entityViews.selections) {
tasks.push(entityViewService.assignEntityViewToEdge(edgeId, entityViewId));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData() {
return vm.entityViews.data.length == 0 && !vm.entityViews.hasNext;
}
function hasData() {
return vm.entityViews.data.length > 0;
}
function toggleEntityViewSelection($event, entityView) {
$event.stopPropagation();
var selected = angular.isDefined(entityView.selected) && entityView.selected;
entityView.selected = !selected;
if (entityView.selected) {
vm.entityViews.selections[entityView.id.id] = true;
vm.entityViews.selectedCount++;
} else {
delete vm.entityViews.selections[entityView.id.id];
vm.entityViews.selectedCount--;
}
}
function searchEntityViewTextUpdated() {
vm.entityViews = {
pageSize: vm.entityViews.pageSize,
data: [],
nextPageLink: {
limit: vm.entityViews.pageSize,
textSearch: vm.searchText
},
selections: {},
selectedCount: 0,
hasNext: true,
pending: false
};
}
}

77
ui/src/app/entity-view/add-entity-views-to-edge.tpl.html

@ -1,77 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'edge.assign-to-edge' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>entity-view.assign-entity-view-to-edge</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>entity-view.assign-entity-view-to-edge-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="entity-view-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchEntityViewTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">entity-view.no-entity-views-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="entityView in vm.theEntityViews" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleEntityViewSelection($event, entityView)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="entityView.selected"></md-checkbox>
<span> {{ entityView.name }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.entityViews.selectedCount == 0" type="submit"
class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

23
ui/src/app/event/event-header-edge-event.tpl.html

@ -1,23 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div translate class="tb-cell" flex="20">event.event-time</div>
<div translate class="tb-cell" flex="20">event.event-type</div>
<div translate class="tb-cell" flex="40">edge.event-action</div>
<div translate class="tb-cell" flex="20">edge.entity-id</div>
<div translate class="tb-cell" flex="15">edge.status</div>
<div translate class="tb-cell" flex="10">edge.entity-info</div>

37
ui/src/app/event/event-row-edge-event.tpl.html

@ -1,37 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="tb-cell" flex="20">{{ event.createdTime | date : 'yyyy-MM-dd HH:mm:ss' }}</div>
<div class="tb-cell" flex="20">{{ event.type }}</div>
<div class="tb-cell" flex="40">{{ event.action }}</div>
<div class="tb-cell" flex="20">{{ event.entityId }}</div>
<div class="tb-cell" flex="15" ng-style="{'color': statusColor}">{{ updateStatus(event.createdTime) }}</div>
<div class="tb-cell" flex="10">
<md-button ng-if="checkEdgeEventType(event.type)" class="md-icon-button md-primary"
ng-click="showEdgeEntityContent($event, 'edge.entity-info', 'JSON')"
aria-label="{{ 'action.view' | translate }}">
<md-tooltip md-direction="top">
{{ 'action.view' | translate }}
</md-tooltip>
<md-icon aria-label="{{ 'action.view' | translate }}"
class="material-icons">
more_horiz
</md-icon>
</md-button>
</div>

122
ui/src/app/rulechain/add-rulechains-to-edge.controller.js

@ -1,122 +0,0 @@
/*
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialog, $q, $filter, edgeId, ruleChains) {
var vm = this;
vm.ruleChains = ruleChains;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.hasData = hasData;
vm.noData = noData;
vm.searchRuleChainTextUpdated = searchRuleChainTextUpdated;
vm.toggleRuleChainSelection = toggleRuleChainSelection;
vm.theRuleChains = {
getItemAtIndex: function (index) {
if (index > vm.ruleChains.data.length) {
vm.theRuleChains.fetchMoreItems_(index);
return null;
}
var item = vm.ruleChains.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.ruleChains.hasNext) {
return vm.ruleChains.data.length + vm.ruleChains.nextPageLink.limit;
} else {
return vm.ruleChains.data.length;
}
},
fetchMoreItems_: function () {
if (vm.ruleChains.hasNext && !vm.ruleChains.pending) {
vm.ruleChains.pending = true;
ruleChainService.getEdgesRuleChains(vm.ruleChains.nextPageLink).then(
function success(ruleChains) {
vm.ruleChains.data = vm.ruleChains.data.concat(ruleChains.data);
vm.ruleChains.nextPageLink = ruleChains.nextPageLink;
vm.ruleChains.hasNext = ruleChains.hasNext;
if (vm.ruleChains.hasNext) {
vm.ruleChains.nextPageLink.limit = vm.ruleChains.pageSize;
}
vm.ruleChains.pending = false;
},
function fail() {
vm.ruleChains.hasNext = false;
vm.ruleChains.pending = false;
});
}
}
}
function cancel () {
$mdDialog.cancel();
}
function assign () {
var tasks = [];
for (var ruleChainId in vm.ruleChains.selections) {
tasks.push(ruleChainService.assignRuleChainToEdge(edgeId, ruleChainId));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData () {
return vm.ruleChains.data.length == 0 && !vm.ruleChains.hasNext;
}
function hasData () {
return vm.ruleChains.data.length > 0;
}
function toggleRuleChainSelection ($event, ruleChain) {
$event.stopPropagation();
var selected = angular.isDefined(ruleChain.selected) && ruleChain.selected;
ruleChain.selected = !selected;
if (ruleChain.selected) {
vm.ruleChains.selections[ruleChain.id.id] = true;
vm.ruleChains.selectedCount++;
} else {
delete vm.ruleChains.selections[ruleChain.id.id];
vm.ruleChains.selectedCount--;
}
}
function searchRuleChainTextUpdated () {
vm.ruleChains = {
pageSize: vm.ruleChains.pageSize,
data: [],
nextPageLink: {
limit: vm.ruleChains.pageSize,
textSearch: vm.searchText
},
selections: {},
selectedCount: 0,
hasNext: true,
pending: false
};
}
}

77
ui/src/app/rulechain/add-rulechains-to-edge.tpl.html

@ -1,77 +0,0 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'rulechain.assign-rulechain-to-edge-title' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>rulechain.assign-rulechain-to-edge-title</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>rulechain.assign-rulechain-to-edge-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="rulechain-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchRuleChainTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">rulechain.no-rulechains-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="ruleChain in vm.theRuleChains" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleRuleChainSelection($event, ruleChain)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="ruleChain.selected"></md-checkbox>
<span> {{ ruleChain.name }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.ruleChains.selectedCount == 0" type="submit"
class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>
Loading…
Cancel
Save