Browse Source

Asset, Device and Entity View assign to edge

pull/2818/head
Volodymyr Babak 7 years ago
parent
commit
5e46f3e7af
  1. 92
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  2. 87
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  3. 18
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  4. 82
      application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
  5. 9
      common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
  6. 9
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  7. 12
      common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
  8. 13
      common/data/src/main/java/org/thingsboard/server/common/data/Device.java
  9. 2
      common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
  10. 13
      common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java
  11. 15
      common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntry.java
  12. 20
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
  13. 38
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  14. 27
      dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java
  15. 27
      dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java
  16. 21
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  17. 39
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  18. 2
      dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java
  19. 28
      dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
  20. 27
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
  21. 45
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
  22. 3
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  23. 20
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java
  24. 21
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceEntity.java
  25. 15
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java
  26. 11
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java
  27. 10
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceEntity.java
  28. 14
      dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java
  29. 21
      dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java
  30. 25
      dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java
  31. 22
      dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
  32. 25
      dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
  33. 22
      dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java
  34. 28
      dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java
  35. 8
      msa/js-executor/package-lock.json
  36. 10
      msa/web-ui/package-lock.json
  37. 58
      ui/src/app/api/asset.service.js
  38. 52
      ui/src/app/api/device.service.js
  39. 49
      ui/src/app/api/entity-view.service.js
  40. 123
      ui/src/app/asset/add-assets-to-edge.controller.js
  41. 77
      ui/src/app/asset/add-assets-to-edge.tpl.html
  42. 209
      ui/src/app/asset/asset.controller.js
  43. 23
      ui/src/app/asset/asset.routes.js
  44. 123
      ui/src/app/asset/assign-to-edge.controller.js
  45. 76
      ui/src/app/asset/assign-to-edge.tpl.html
  46. 4
      ui/src/app/asset/index.js
  47. 123
      ui/src/app/device/add-devices-to-edge.controller.js
  48. 77
      ui/src/app/device/add-devices-to-edge.tpl.html
  49. 123
      ui/src/app/device/assign-to-edge.controller.js
  50. 76
      ui/src/app/device/assign-to-edge.tpl.html
  51. 211
      ui/src/app/device/device.controller.js
  52. 23
      ui/src/app/device/device.routes.js
  53. 4
      ui/src/app/device/index.js
  54. 62
      ui/src/app/edge/edge.controller.js
  55. 123
      ui/src/app/entity-view/add-entity-views-to-edge.controller.js
  56. 77
      ui/src/app/entity-view/add-entity-views-to-edge.tpl.html
  57. 123
      ui/src/app/entity-view/assign-to-edge.controller.js
  58. 76
      ui/src/app/entity-view/assign-to-edge.tpl.html
  59. 208
      ui/src/app/entity-view/entity-view.controller.js
  60. 23
      ui/src/app/entity-view/entity-view.routes.js
  61. 4
      ui/src/app/entity-view/index.js
  62. 35
      ui/src/app/locale/locale.constant-en_US.json

92
application/src/main/java/org/thingsboard/server/controller/AssetController.java

@ -32,14 +32,14 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetSearchQuery;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.security.model.SecurityUser;
@ -330,4 +330,92 @@ public class AssetController extends BaseController {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST)
@ResponseBody
public Asset assignAssetToEdge(@PathVariable("edgeId") String strEdgeId,
@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
checkParameter(ASSET_ID, strAssetId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
AssetId assetId = new AssetId(toUUID(strAssetId));
checkAssetId(assetId, Operation.ASSIGN_TO_EDGE);
Asset savedAsset = checkNotNull(assetService.assignAssetToEdge(getTenantId(), assetId, edgeId));
logEntityAction(assetId, savedAsset,
savedAsset.getCustomerId(),
ActionType.ASSIGNED_TO_EDGE, null, strAssetId, strEdgeId, edge.getName());
return savedAsset;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ASSET), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strAssetId, strEdgeId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/asset/{assetId}", method = RequestMethod.DELETE)
@ResponseBody
public Asset unassignAssetFromEdge(@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter(ASSET_ID, strAssetId);
try {
AssetId assetId = new AssetId(toUUID(strAssetId));
Asset asset = checkAssetId(assetId, Operation.UNASSIGN_FROM_EDGE);
if (asset.getEdgeId() == null || asset.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Asset isn't assigned to any edge!");
}
Edge edge = checkEdgeId(asset.getEdgeId(), Operation.READ);
Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId));
logEntityAction(assetId, asset,
asset.getCustomerId(),
ActionType.UNASSIGNED_FROM_EDGE, null, strAssetId, edge.getId().toString(), edge.getName());
return savedAsset;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ASSET), null,
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strAssetId);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/assets", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData<Asset> getEdgeAssets(
@PathVariable("edgeId") String strEdgeId,
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length()>0) {
return checkNotNull(assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
} else {
return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
}
}

87
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -37,9 +37,11 @@ import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
@ -59,6 +61,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
@RestController
@RequestMapping("/api")
public class DeviceController extends BaseController {
@ -479,4 +483,87 @@ public class DeviceController extends BaseController {
}
return DataConstants.DEFAULT_SECRET_KEY;
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public Device assignDeviceToEdge(@PathVariable(EDGE_ID) String strEdgeId,
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(DEVICE_ID, strDeviceId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
checkDeviceId(deviceId, Operation.ASSIGN_TO_EDGE);
Device savedDevice = checkNotNull(deviceService.assignDeviceToEdge(getCurrentUser().getTenantId(), deviceId, edgeId));
logEntityAction(deviceId, savedDevice,
savedDevice.getCustomerId(),
ActionType.ASSIGNED_TO_EDGE, null, strDeviceId, strEdgeId, edge.getName());
return savedDevice;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strDeviceId, strEdgeId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/device/{deviceId}", method = RequestMethod.DELETE)
@ResponseBody
public Device unassignDeviceFromEdge(@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(DEVICE_ID, strDeviceId);
try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId, Operation.UNASSIGN_FROM_EDGE);
if (device.getEdgeId() == null || device.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Device isn't assigned to any edge!");
}
Edge edge = checkEdgeId(device.getEdgeId(), Operation.READ);
Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId));
logEntityAction(deviceId, device,
device.getCustomerId(),
ActionType.UNASSIGNED_FROM_EDGE, null, strDeviceId, edge.getId().toString(), edge.getName());
return savedDevice;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), null,
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strDeviceId);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/devices", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData<Device> getEdgeDevices(
@PathVariable("edgeId") String strEdgeId,
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length()>0) {
return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
} else {
return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
}
}

18
application/src/main/java/org/thingsboard/server/controller/EdgeController.java

@ -54,7 +54,7 @@ import java.util.stream.Collectors;
@RequestMapping("/api")
public class EdgeController extends BaseController {
private static final String EDGE_ID = "edgeId";
public static final String EDGE_ID = "edgeId";
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET)
@ -118,6 +118,22 @@ public class EdgeController extends BaseController {
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edges", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData<Edge> getEdges(@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/edge/{edgeId}", method = RequestMethod.POST)
@ResponseBody

82
application/src/main/java/org/thingsboard/server/controller/EntityViewController.java

@ -35,9 +35,11 @@ import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
@ -59,6 +61,7 @@ import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
/**
* Created by Victor Basanets on 8/28/2017.
@ -365,4 +368,83 @@ public class EntityViewController extends BaseController {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.POST)
@ResponseBody
public EntityView assignEntityViewToEdge(@PathVariable(EDGE_ID) String strEdgeId,
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
checkEntityViewId(entityViewId, Operation.ASSIGN_TO_EDGE);
EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToEdge(getTenantId(), entityViewId, edgeId));
logEntityAction(entityViewId, savedEntityView,
savedEntityView.getCustomerId(),
ActionType.ASSIGNED_TO_EDGE, null, strEntityViewId, strEdgeId, edge.getName());
return savedEntityView;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strEntityViewId, strEdgeId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/entityView/{entityViewId}", method = RequestMethod.DELETE)
@ResponseBody
public EntityView unassignEntityViewFromEdge(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
try {
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_EDGE);
if (entityView.getEdgeId() == null || entityView.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Entity View isn't assigned to any edge!");
}
Edge edge = checkEdgeId(entityView.getEdgeId(), Operation.READ);
EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId));
logEntityAction(entityViewId, entityView,
entityView.getCustomerId(),
ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, edge.getId().toString(), edge.getName());
return savedEntityView;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strEntityViewId);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/entityViews", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public TextPageData<EntityView> getEdgeEntityViews(
@PathVariable("edgeId") String strEdgeId,
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length()>0) {
return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
} else {
return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
}
}

9
common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java

@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetSearchQuery;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
@ -63,4 +64,12 @@ public interface AssetService {
ListenableFuture<List<Asset>> findAssetsByQuery(TenantId tenantId, AssetSearchQuery query);
ListenableFuture<List<EntitySubtype>> findAssetTypesByTenantId(TenantId tenantId);
Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId);
Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId);
TextPageData<Asset> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink);
TextPageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink);
}

9
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
@ -63,4 +64,12 @@ public interface DeviceService {
ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId);
Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId);
TextPageData<Device> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink);
TextPageData<Device> findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink);
}

12
common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java

@ -18,9 +18,9 @@ package org.thingsboard.server.dao.entityview;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
@ -65,4 +65,14 @@ public interface EntityViewService {
void deleteEntityViewsByTenantId(TenantId tenantId);
ListenableFuture<List<EntitySubtype>> findEntityViewTypesByTenantId(TenantId tenantId);
EntityView assignEntityViewToEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId);
EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId);
TextPageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink);
TextPageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink);
}

13
common/data/src/main/java/org/thingsboard/server/common/data/Device.java

@ -18,6 +18,7 @@ package org.thingsboard.server.common.data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
@EqualsAndHashCode(callSuper = true)
@ -27,6 +28,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
private TenantId tenantId;
private CustomerId customerId;
private EdgeId edgeId;
private String name;
private String type;
private String label;
@ -46,6 +48,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.name = device.getName();
this.type = device.getType();
this.label = device.getLabel();
this.edgeId = device.getEdgeId();
}
public TenantId getTenantId() {
@ -64,6 +67,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.customerId = customerId;
}
public EdgeId getEdgeId() {
return edgeId;
}
public void setEdgeId(EdgeId edgeId) {
this.edgeId = edgeId;
}
@Override
public String getName() {
return name;
@ -101,6 +112,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
builder.append(tenantId);
builder.append(", customerId=");
builder.append(customerId);
builder.append(", edgeId=");
builder.append(edgeId);
builder.append(", name=");
builder.append(name);
builder.append(", type=");

2
common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java

@ -19,6 +19,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
@ -39,6 +40,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
private EntityId entityId;
private TenantId tenantId;
private CustomerId customerId;
private EdgeId edgeId;
private String name;
private String type;
private TelemetryEntityView keys;

13
common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java

@ -20,6 +20,7 @@ import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
@EqualsAndHashCode(callSuper = true)
@ -31,6 +32,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
private CustomerId customerId;
private String name;
private String type;
private EdgeId edgeId;
public Asset() {
super();
@ -46,6 +48,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
this.customerId = asset.getCustomerId();
this.name = asset.getName();
this.type = asset.getType();
this.edgeId = asset.getEdgeId();
}
public TenantId getTenantId() {
@ -64,6 +67,14 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
this.customerId = customerId;
}
public EdgeId getEdgeId() {
return edgeId;
}
public void setEdgeId(EdgeId edgeId) {
this.edgeId = edgeId;
}
@Override
public String getName() {
return name;
@ -93,6 +104,8 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
builder.append(tenantId);
builder.append(", customerId=");
builder.append(customerId);
builder.append(", edgeId=");
builder.append(edgeId);
builder.append(", name=");
builder.append(name);
builder.append(", type=");

15
common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntry.java

@ -1,3 +1,18 @@
/**
* Copyright © 2016-2019 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.edge;
import lombok.Data;

20
dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java

@ -115,4 +115,24 @@ public interface AssetDao extends Dao<Asset> {
*/
ListenableFuture<List<EntitySubtype>> findTenantAssetTypesAsync(UUID tenantId);
/**
* Find assets by tenantId, customerId and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param pageLink the page link
* @return the list of asset objects
*/
List<Asset> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink);
/**
* Find assets by tenantId, customerId, type and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param type the type
* @param pageLink the page link
* @return the list of asset objects
*/
List<Asset> findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink);
}

38
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetSearchQuery;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
@ -70,6 +71,8 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_ASSET_ID = "Incorrect assetId ";
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
@Autowired
private AssetDao assetDao;
@ -260,6 +263,41 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
});
}
@Override
public Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId) {
Asset asset = findAssetById(tenantId, assetId);
asset.setEdgeId(edgeId);
return saveAsset(asset);
}
@Override
public Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId) {
Asset asset = findAssetById(tenantId, assetId);
asset.setEdgeId(null);
return saveAsset(asset);
}
@Override
public TextPageData<Asset> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
List<Asset> assets = assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
return new TextPageData<>(assets, pageLink);
}
@Override
public TextPageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
List<Asset> assets = assetDao.findAssetsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
return new TextPageData<>(assets, pageLink);
}
private DataValidator<Asset> assetValidator =
new DataValidator<Asset>() {

27
dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java

@ -188,4 +188,31 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
});
}
@Override
public List<Asset> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) {
// log.debug("Try to find assets by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
// List<AssetEntity> assetEntities = findPageWithTextSearch(new TenantId(tenantId), ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
// Arrays.asList(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
// eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
// pageLink);
//
// log.trace("Found assets [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", assetEntities, tenantId, customerId, pageLink);
// return DaoUtil.convertDataList(assetEntities);
throw new UnsupportedOperationException("Cassandra is not supported yet");
}
@Override
public List<Asset> findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) {
// log.debug("Try to find assets by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
// List<AssetEntity> assetEntities = findPageWithTextSearch(new TenantId(tenantId), ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
// Arrays.asList(eq(ASSET_TYPE_PROPERTY, type),
// eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
// eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
// pageLink);
//
// log.trace("Found assets [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, customerId, type, pageLink);
// return DaoUtil.convertDataList(assetEntities);
throw new UnsupportedOperationException("Cassandra is not supported yet");
}
}

27
dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java

@ -188,4 +188,31 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
});
}
@Override
public List<Device> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) {
// log.debug("Try to find devices by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
// List<DeviceEntity> deviceEntities = findPageWithTextSearch(new TenantId(tenantId), DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
// Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
// eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
// pageLink);
//
// log.trace("Found devices [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, pageLink);
// return DaoUtil.convertDataList(deviceEntities);
throw new UnsupportedOperationException("Cassandra is not supported yet");
}
@Override
public List<Device> findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) {
// log.debug("Try to find devices by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
// List<DeviceEntity> deviceEntities = findPageWithTextSearch(new TenantId(tenantId), DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
// Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type),
// eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
// eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
// pageLink);
//
// log.trace("Found devices [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, type, pageLink);
// return DaoUtil.convertDataList(deviceEntities);
throw new UnsupportedOperationException("Cassandra is not supported yet");
}
}

21
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java

@ -115,4 +115,25 @@ public interface DeviceDao extends Dao<Device> {
* @return the list of tenant device type objects
*/
ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId);
/**
* Find devices by tenantId, edgeId and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param pageLink the page link
* @return the list of device objects
*/
List<Device> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink);
/**
* Find devices by tenantId, edgeId, type and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param type the type
* @param pageLink the page link
* @return the list of device objects
*/
List<Device> findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink);
}

39
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -34,8 +34,10 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
@ -77,6 +79,8 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_DEVICE_ID = "Incorrect deviceId ";
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
@Autowired
private DeviceDao deviceDao;
@ -291,6 +295,41 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
});
}
@Override
public Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId) {
Device device = findDeviceById(tenantId, deviceId);
device.setEdgeId(edgeId);
return saveDevice(device);
}
@Override
public Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId) {
Device device = findDeviceById(tenantId, deviceId);
device.setEdgeId(null);
return saveDevice(device);
}
@Override
public TextPageData<Device> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
List<Device> devices = deviceDao.findDevicesByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
return new TextPageData<>(devices, pageLink);
}
@Override
public TextPageData<Device> findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
List<Device> devices = deviceDao.findDevicesByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
return new TextPageData<>(devices, pageLink);
}
private DataValidator<Device> deviceValidator =
new DataValidator<Device>() {

2
dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java

@ -5,7 +5,7 @@
* 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
* 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,

28
dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java

@ -183,4 +183,32 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
}
});
}
@Override
public List<EntityView> findEntityViewsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) {
// log.debug("Try to find entity views by tenantId [{}], customerId[{}] and pageLink [{}]",
// tenantId, customerId, pageLink);
// List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(new TenantId(tenantId),
// ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF,
// Arrays.asList(eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)),
// pageLink);
// log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}] and pageLink [{}]",
// entityViewEntities, tenantId, customerId, pageLink);
// return DaoUtil.convertDataList(entityViewEntities);
throw new UnsupportedOperationException("Cassandra is not supported yet");
}
@Override
public List<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) {
// log.debug("Try to find entity views by tenantId [{}], customerId[{}], type [{}] and pageLink [{}]",
// tenantId, customerId, type, pageLink);
// List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(new TenantId(tenantId),
// ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF,
// Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)),
// pageLink);
// log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]",
// entityViewEntities, tenantId, customerId, type, pageLink);
// return DaoUtil.convertDataList(entityViewEntities);
throw new UnsupportedOperationException("Cassandra is not supported yet");
}
}

27
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java

@ -16,7 +16,6 @@
package org.thingsboard.server.dao.entityview;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.TenantId;
@ -103,4 +102,30 @@ public interface EntityViewDao extends Dao<EntityView> {
*/
ListenableFuture<List<EntitySubtype>> findTenantEntityViewTypesAsync(UUID tenantId);
/**
* Find entity views by tenantId, edgeId and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param pageLink the page link
* @return the list of entity view objects
*/
List<EntityView> findEntityViewsByTenantIdAndEdgeId(UUID tenantId,
UUID edgeId,
TextPageLink pageLink);
/**
* Find entity views by tenantId, edgeId, type and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param type the type
* @param pageLink the page link
* @return the list of entity view objects
*/
List<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId,
UUID edgeId,
String type,
TextPageLink pageLink);
}

45
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java

@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
@ -75,6 +76,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_ENTITY_VIEW_ID = "Incorrect entityViewId ";
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
@Autowired
private EntityViewDao entityViewDao;
@ -282,6 +284,49 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
});
}
@CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override
public EntityView assignEntityViewToEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId) {
EntityView entityView = findEntityViewById(tenantId, entityViewId);
entityView.setEdgeId(edgeId);
return saveEntityView(entityView);
}
@CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override
public EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId) {
EntityView entityView = findEntityViewById(tenantId, entityViewId);
entityView.setEdgeId(null);
return saveEntityView(entityView);
}
@Override
public TextPageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId,
TextPageLink pageLink) {
log.trace("Executing findEntityViewsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}]," +
" pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
List<EntityView> entityViews = entityViewDao.findEntityViewsByTenantIdAndEdgeId(tenantId.getId(),
edgeId.getId(), pageLink);
return new TextPageData<>(entityViews, pageLink);
}
@Override
public TextPageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink) {
log.trace("Executing findEntityViewsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}]," +
" pageLink [{}], type [{}]", tenantId, edgeId, pageLink, type);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink);
validateString(type, "Incorrect type " + type);
List<EntityView> entityViews = entityViewDao.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId.getId(),
edgeId.getId(), type, pageLink);
return new TextPageData<>(entityViews, pageLink);
}
private DataValidator<EntityView> entityViewValidator =
new DataValidator<EntityView>() {

3
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -137,6 +137,7 @@ public class ModelConstants {
public static final String DEVICE_TYPE_PROPERTY = "type";
public static final String DEVICE_LABEL_PROPERTY = "label";
public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String DEVICE_EDGE_ID_PROPERTY = "edge_id";
public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text";
@ -152,6 +153,7 @@ public class ModelConstants {
public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY;
public static final String ENTITY_VIEW_EDGE_ID_PROPERTY = "edge_id";
public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF = "entity_view_by_tenant_and_customer";
public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF = "entity_view_by_tenant_and_customer_and_type";
public static final String ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF = "entity_view_by_tenant_and_entity_id";
@ -198,6 +200,7 @@ public class ModelConstants {
public static final String ASSET_NAME_PROPERTY = "name";
public static final String ASSET_TYPE_PROPERTY = "type";
public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String ASSET_EDGE_ID_PROPERTY = "edge_id";
public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text";
public static final String ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_by_type_and_search_text";

20
dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java

@ -25,6 +25,7 @@ import lombok.ToString;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.server.dao.model.type.JsonCodec;
@ -34,6 +35,7 @@ import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_ADDITIONAL_INFO_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_EDGE_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TYPE_PROPERTY;
@ -61,6 +63,10 @@ public final class AssetEntity implements SearchTextEntity<Asset> {
@Column(name = ASSET_TYPE_PROPERTY)
private String type;
@PartitionKey(value = 4)
@Column(name = ASSET_EDGE_ID_PROPERTY)
private UUID edgeId;
@Column(name = ASSET_NAME_PROPERTY)
private String name;
@ -84,6 +90,9 @@ public final class AssetEntity implements SearchTextEntity<Asset> {
if (asset.getCustomerId() != null) {
this.customerId = asset.getCustomerId().getId();
}
if (asset.getEdgeId() != null) {
this.edgeId = asset.getEdgeId().getId();
}
this.name = asset.getName();
this.type = asset.getType();
this.additionalInfo = asset.getAdditionalInfo();
@ -113,6 +122,14 @@ public final class AssetEntity implements SearchTextEntity<Asset> {
this.customerId = customerId;
}
public UUID getEdgeId() {
return edgeId;
}
public void setEdgeId(UUID edgeId) {
this.edgeId = edgeId;
}
public String getName() {
return name;
}
@ -161,6 +178,9 @@ public final class AssetEntity implements SearchTextEntity<Asset> {
if (customerId != null) {
asset.setCustomerId(new CustomerId(customerId));
}
if (edgeId != null) {
asset.setEdgeId(new EdgeId(edgeId));
}
asset.setName(name);
asset.setType(type);
asset.setAdditionalInfo(additionalInfo);

21
dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceEntity.java

@ -25,6 +25,7 @@ import lombok.ToString;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.server.dao.model.type.JsonCodec;
@ -54,6 +55,10 @@ public final class DeviceEntity implements SearchTextEntity<Device> {
@Column(name = DEVICE_TYPE_PROPERTY)
private String type;
@PartitionKey(value = 4)
@Column(name = DEVICE_EDGE_ID_PROPERTY)
private UUID edgeId;
@Column(name = DEVICE_NAME_PROPERTY)
private String name;
@ -80,6 +85,9 @@ public final class DeviceEntity implements SearchTextEntity<Device> {
if (device.getCustomerId() != null) {
this.customerId = device.getCustomerId().getId();
}
if (device.getEdgeId() != null) {
this.edgeId = device.getEdgeId().getId();
}
this.name = device.getName();
this.type = device.getType();
this.label = device.getLabel();
@ -109,7 +117,15 @@ public final class DeviceEntity implements SearchTextEntity<Device> {
public void setCustomerId(UUID customerId) {
this.customerId = customerId;
}
public UUID getEdgeId() {
return edgeId;
}
public void setEdgeId(UUID edgeId) {
this.edgeId = edgeId;
}
public String getName() {
return name;
}
@ -158,6 +174,9 @@ public final class DeviceEntity implements SearchTextEntity<Device> {
if (customerId != null) {
device.setCustomerId(new CustomerId(customerId));
}
if (edgeId != null) {
device.setEdgeId(new EdgeId(edgeId));
}
device.setName(name);
device.setType(type);
device.setLabel(label);

15
dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java

@ -29,6 +29,7 @@ import org.hibernate.annotations.Type;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
@ -41,14 +42,12 @@ import javax.persistence.Enumerated;
import java.io.IOException;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_EDGE_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY;
/**
* Created by Victor Basanets on 8/31/2017.
*/
@Data
@Table(name = ENTITY_VIEW_TABLE_FAMILY_NAME)
@EqualsAndHashCode
@ -72,6 +71,10 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
@Column(name = DEVICE_TYPE_PROPERTY)
private String type;
@PartitionKey(value = 4)
@Column(name = DEVICE_EDGE_ID_PROPERTY)
private UUID edgeId;
@Enumerated(EnumType.STRING)
@Column(name = ENTITY_TYPE_PROPERTY)
private EntityType entityType;
@ -118,6 +121,9 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
if (entityView.getCustomerId() != null) {
this.customerId = entityView.getCustomerId().getId();
}
if (entityView.getEdgeId() != null) {
this.edgeId = entityView.getEdgeId().getId();
}
this.type = entityView.getType();
this.name = entityView.getName();
try {
@ -149,6 +155,9 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
if (customerId != null) {
entityView.setCustomerId(new CustomerId(customerId));
}
if (edgeId != null) {
entityView.setEdgeId(new EdgeId(edgeId));
}
entityView.setType(type);
entityView.setName(name);
try {

11
dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java

@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
@ -37,6 +38,7 @@ import javax.persistence.Table;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_EDGE_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TYPE_PROPERTY;
@ -55,6 +57,9 @@ public final class AssetEntity extends BaseSqlEntity<Asset> implements SearchTex
@Column(name = ASSET_CUSTOMER_ID_PROPERTY)
private String customerId;
@Column(name = ASSET_EDGE_ID_PROPERTY)
private String edgeId;
@Column(name = ASSET_NAME_PROPERTY)
private String name;
@ -82,6 +87,9 @@ public final class AssetEntity extends BaseSqlEntity<Asset> implements SearchTex
if (asset.getCustomerId() != null) {
this.customerId = UUIDConverter.fromTimeUUID(asset.getCustomerId().getId());
}
if (asset.getEdgeId() != null) {
this.edgeId = UUIDConverter.fromTimeUUID(asset.getEdgeId().getId());
}
this.name = asset.getName();
this.type = asset.getType();
this.additionalInfo = asset.getAdditionalInfo();
@ -111,6 +119,9 @@ public final class AssetEntity extends BaseSqlEntity<Asset> implements SearchTex
if (customerId != null) {
asset.setCustomerId(new CustomerId(UUIDConverter.fromString(customerId)));
}
if (edgeId != null) {
asset.setEdgeId(new EdgeId(UUIDConverter.fromString(edgeId)));
}
asset.setName(name);
asset.setType(type);
asset.setAdditionalInfo(additionalInfo);

10
dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceEntity.java

@ -24,6 +24,7 @@ import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
@ -47,6 +48,9 @@ public final class DeviceEntity extends BaseSqlEntity<Device> implements SearchT
@Column(name = ModelConstants.DEVICE_CUSTOMER_ID_PROPERTY)
private String customerId;
@Column(name = ModelConstants.DEVICE_EDGE_ID_PROPERTY)
private String edgeId;
@Column(name = ModelConstants.DEVICE_TYPE_PROPERTY)
private String type;
@ -77,6 +81,9 @@ public final class DeviceEntity extends BaseSqlEntity<Device> implements SearchT
if (device.getCustomerId() != null) {
this.customerId = toString(device.getCustomerId().getId());
}
if (device.getEdgeId() != null) {
this.edgeId = toString(device.getEdgeId().getId());
}
this.name = device.getName();
this.type = device.getType();
this.label = device.getLabel();
@ -103,6 +110,9 @@ public final class DeviceEntity extends BaseSqlEntity<Device> implements SearchT
if (customerId != null) {
device.setCustomerId(new CustomerId(toUUID(customerId)));
}
if (edgeId != null) {
device.setEdgeId(new EdgeId(toUUID(edgeId)));
}
device.setName(name);
device.setType(type);
device.setLabel(label);

14
dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java

@ -26,6 +26,7 @@ import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
@ -44,10 +45,6 @@ import java.io.IOException;
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY;
/**
* Created by Victor Basanets on 8/30/2017.
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@ -69,6 +66,9 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc
@Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY)
private String customerId;
@Column(name = ModelConstants.ENTITY_VIEW_EDGE_ID_PROPERTY)
private String edgeId;
@Column(name = ModelConstants.DEVICE_TYPE_PROPERTY)
private String type;
@ -111,6 +111,9 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc
if (entityView.getCustomerId() != null) {
this.customerId = toString(entityView.getCustomerId().getId());
}
if (entityView.getEdgeId() != null) {
this.edgeId = toString(entityView.getEdgeId().getId());
}
this.type = entityView.getType();
this.name = entityView.getName();
try {
@ -148,6 +151,9 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc
if (customerId != null) {
entityView.setCustomerId(new CustomerId(toUUID(customerId)));
}
if (edgeId != null) {
entityView.setEdgeId(new EdgeId(toUUID(edgeId)));
}
entityView.setType(type);
entityView.setName(name);
try {

21
dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java

@ -78,4 +78,25 @@ public interface AssetRepository extends CrudRepository<AssetEntity, String> {
@Query("SELECT DISTINCT a.type FROM AssetEntity a WHERE a.tenantId = :tenantId")
List<String> findTenantAssetTypes(@Param("tenantId") String tenantId);
@Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " +
"AND a.edgeId = :edgeId " +
"AND LOWER(a.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " +
"AND a.id > :idOffset ORDER BY a.id")
List<AssetEntity> findByTenantIdAndEdgeId(@Param("tenantId") String tenantId,
@Param("edgeId") String edgeId,
@Param("textSearch") String textSearch,
@Param("idOffset") String idOffset,
Pageable pageable);
@Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " +
"AND a.edgeId = :edgeId AND a.type = :type " +
"AND LOWER(a.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " +
"AND a.id > :idOffset ORDER BY a.id")
List<AssetEntity> findByTenantIdAndEdgeIdAndType(@Param("tenantId") String tenantId,
@Param("edgeId") String edgeId,
@Param("type") String type,
@Param("textSearch") String textSearch,
@Param("idOffset") String idOffset,
Pageable pageable);
}

25
dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java

@ -139,4 +139,29 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im
}
return list;
}
@Override
public List<Asset> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) {
return DaoUtil.convertDataList(assetRepository
.findByTenantIdAndEdgeId(
fromTimeUUID(tenantId),
fromTimeUUID(edgeId),
Objects.toString(pageLink.getTextSearch(), ""),
pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()),
new PageRequest(0, pageLink.getLimit())));
}
@Override
public List<Asset> findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) {
return DaoUtil.convertDataList(assetRepository
.findByTenantIdAndEdgeIdAndType(
fromTimeUUID(tenantId),
fromTimeUUID(edgeId),
type,
Objects.toString(pageLink.getTextSearch(), ""),
pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()),
new PageRequest(0, pageLink.getLimit())));
}
}

22
dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java

@ -79,4 +79,26 @@ public interface DeviceRepository extends CrudRepository<DeviceEntity, String> {
List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndIdIn(String tenantId, String customerId, List<String> deviceIds);
List<DeviceEntity> findDevicesByTenantIdAndIdIn(String tenantId, List<String> deviceIds);
@Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " +
"AND d.edgeId = :edgeId " +
"AND LOWER(d.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " +
"AND d.id > :idOffset ORDER BY d.id")
List<DeviceEntity> findByTenantIdAndEdgeId(@Param("tenantId") String tenantId,
@Param("edgeId") String edgeId,
@Param("searchText") String searchText,
@Param("idOffset") String idOffset,
Pageable pageable);
@Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " +
"AND d.edgeId = :edgeId " +
"AND d.type = :type " +
"AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " +
"AND d.id > :idOffset ORDER BY d.id")
List<DeviceEntity> findByTenantIdAndEdgeIdAndType(@Param("tenantId") String tenantId,
@Param("edgeId") String edgeId,
@Param("type") String type,
@Param("textSearch") String textSearch,
@Param("idOffset") String idOffset,
Pageable pageable);
}

25
dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java

@ -139,4 +139,29 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
}
return list;
}
@Override
public List<Device> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) {
return DaoUtil.convertDataList(
deviceRepository.findByTenantIdAndEdgeId(
fromTimeUUID(tenantId),
fromTimeUUID(edgeId),
Objects.toString(pageLink.getTextSearch(), ""),
pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()),
new PageRequest(0, pageLink.getLimit())));
}
@Override
public List<Device> findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) {
return DaoUtil.convertDataList(
deviceRepository.findByTenantIdAndEdgeIdAndType(
fromTimeUUID(tenantId),
fromTimeUUID(edgeId),
type,
Objects.toString(pageLink.getTextSearch(), ""),
pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()),
new PageRequest(0, pageLink.getLimit())));
}
}

22
dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java

@ -76,4 +76,26 @@ public interface EntityViewRepository extends CrudRepository<EntityViewEntity, S
@Query("SELECT DISTINCT ev.type FROM EntityViewEntity ev WHERE ev.tenantId = :tenantId")
List<String> findTenantEntityViewTypes(@Param("tenantId") String tenantId);
@Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " +
"AND e.edgeId = :edgeId " +
"AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " +
"AND e.id > :idOffset ORDER BY e.id")
List<EntityViewEntity> findByTenantIdAndEdgeId(@Param("tenantId") String tenantId,
@Param("edgeId") String edgeId,
@Param("searchText") String searchText,
@Param("idOffset") String idOffset,
Pageable pageable);
@Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " +
"AND e.edgeId = :edgeId " +
"AND e.type = :type " +
"AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " +
"AND e.id > :idOffset ORDER BY e.id")
List<EntityViewEntity> findByTenantIdAndEdgeIdAndType(@Param("tenantId") String tenantId,
@Param("edgeId") String edgeId,
@Param("type") String type,
@Param("searchText") String searchText,
@Param("idOffset") String idOffset,
Pageable pageable);
}

28
dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java

@ -138,4 +138,32 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity,
}
return list;
}
@Override
public List<EntityView> findEntityViewsByTenantIdAndEdgeId(UUID tenantId,
UUID edgeId,
TextPageLink pageLink) {
return DaoUtil.convertDataList(
entityViewRepository.findByTenantIdAndEdgeId(
fromTimeUUID(tenantId),
fromTimeUUID(edgeId),
Objects.toString(pageLink.getTextSearch(), ""),
pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()),
new PageRequest(0, pageLink.getLimit())
));
}
@Override
public List<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) {
return DaoUtil.convertDataList(
entityViewRepository.findByTenantIdAndEdgeIdAndType(
fromTimeUUID(tenantId),
fromTimeUUID(edgeId),
type,
Objects.toString(pageLink.getTextSearch(), ""),
pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()),
new PageRequest(0, pageLink.getLimit())
));
}
}

8
msa/js-executor/package-lock.json

@ -1261,7 +1261,7 @@
},
"fecha": {
"version": "2.3.3",
"resolved": "http://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
"integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg=="
},
"file-stream-rotator": {
@ -2426,7 +2426,7 @@
},
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"requires": {
"minimist": "^1.2.0"
@ -2947,7 +2947,7 @@
},
"os-homedir": {
"version": "1.0.2",
"resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"optional": true
},
@ -2995,7 +2995,7 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},

10
msa/web-ui/package-lock.json

@ -1149,7 +1149,7 @@
},
"fecha": {
"version": "2.3.3",
"resolved": "http://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
"integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg=="
},
"file-stream-rotator": {
@ -2354,7 +2354,7 @@
},
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"requires": {
"minimist": "^1.2.0"
@ -2581,7 +2581,7 @@
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
@ -2590,7 +2590,7 @@
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
}
@ -2859,7 +2859,7 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},

58
ui/src/app/api/asset.service.js

@ -33,7 +33,10 @@ function AssetService($http, $q, customerService, userService) {
findByQuery: findByQuery,
fetchAssetsByNameFilter: fetchAssetsByNameFilter,
getAssetTypes: getAssetTypes,
findByName: findByName
findByName: findByName,
assignAssetToEdge: assignAssetToEdge,
unassignAssetFromEdge: unassignAssetFromEdge,
getEdgeAssets: getEdgeAssets
}
return service;
@ -289,4 +292,57 @@ function AssetService($http, $q, customerService, userService) {
return deferred.promise;
}
function assignAssetToEdge(edgeId, assetId, ignoreErrors, config) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/asset/' + assetId;
if (!config) {
config = {};
}
config = Object.assign(config, { ignoreErrors: ignoreErrors });
$http.post(url, null, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function unassignAssetFromEdge(assetId, ignoreErrors, config) {
var deferred = $q.defer();
var url = '/api/edge/asset/' + assetId;
if (!config) {
config = {};
}
config = Object.assign(config, { ignoreErrors: ignoreErrors });
$http.delete(url, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getEdgeAssets(edgeId, pageLink, config, type) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/assets?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) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
}

52
ui/src/app/api/device.service.js

@ -43,7 +43,10 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
sendTwoWayRpcCommand: sendTwoWayRpcCommand,
findByQuery: findByQuery,
getDeviceTypes: getDeviceTypes,
findByName: findByName
findByName: findByName,
assignDeviceToEdge: assignDeviceToEdge,
unassignDeviceFromEdge: unassignDeviceFromEdge,
getEdgeDevices: getEdgeDevices
};
return service;
@ -332,4 +335,51 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
});
return deferred.promise;
}
function assignDeviceToEdge(edgeId, deviceId) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/device/' + deviceId;
$http.post(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function unassignDeviceFromEdge(deviceId) {
var deferred = $q.defer();
var url = '/api/edge/device/' + deviceId;
$http.delete(url).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getEdgeDevices(edgeId, pageLink, config, type) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/devices?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) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
}

49
ui/src/app/api/entity-view.service.js

@ -35,7 +35,10 @@ function EntityViewService($http, $q, $window, userService, attributeService, cu
unsubscribeForEntityViewAttributes: unsubscribeForEntityViewAttributes,
findByQuery: findByQuery,
getEntityViewTypes: getEntityViewTypes,
makeEntityViewPublic: makeEntityViewPublic
makeEntityViewPublic: makeEntityViewPublic,
assignEntityViewToEdge: assignEntityViewToEdge,
unassignEntityViewFromEdge: unassignEntityViewFromEdge,
getEdgeEntityViews: getEdgeEntityViews
}
return service;
@ -220,5 +223,49 @@ function EntityViewService($http, $q, $window, userService, attributeService, cu
return deferred.promise;
}
function assignEntityViewToEdge(edgeId, entityViewId) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/entityView/' + entityViewId;
$http.post(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function unassignEntityViewFromEdge(entityViewId) {
var deferred = $q.defer();
var url = '/api/edge/entityView/' + entityViewId;
$http.delete(url).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getEdgeEntityViews(edgeId, pageLink, config, type) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/entityViews?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) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
}

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

@ -0,0 +1,123 @@
/*
* Copyright © 2016-2019 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

@ -0,0 +1,77 @@
<!--
Copyright © 2016-2019 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="{{ 'asset.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>

209
ui/src/app/asset/asset.controller.js

@ -19,6 +19,8 @@ import addAssetTemplate from './add-asset.tpl.html';
import assetCard from './asset-card.tpl.html';
import assignToCustomerTemplate from './assign-to-customer.tpl.html';
import addAssetsToCustomerTemplate from './add-assets-to-customer.tpl.html';
import assignToEdgeTemplate from './assign-to-edge.tpl.html';
import addAssetsToEdgeTemplate from './add-assets-to-edge.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
@ -47,10 +49,11 @@ export function AssetCardController(types) {
/*@ngInject*/
export function AssetController($rootScope, userService, assetService, customerService, $state, $stateParams,
export function AssetController($rootScope, userService, assetService, customerService, edgeService, $state, $stateParams,
$document, $mdDialog, $q, $translate, types, importExport) {
var customerId = $stateParams.customerId;
var edgeId = $stateParams.edgeId;
var assetActionsList = [];
@ -216,6 +219,32 @@ export function AssetController($rootScope, userService, assetService, customerS
}
});
assetActionsList.push({
onAction: function ($event, item) {
assignToEdge($event, [ item.id.id ]);
},
name: function() { return $translate.instant('action.assign') },
details: function() { return $translate.instant('asset.assign-to-edge') },
icon: "wifi_tethering",
isEnabled: function(asset) {
return asset && (!asset.edgeId || asset.edgeId.id === types.id.nullUid);
}
}
);
assetActionsList.push({
onAction: function ($event, item) {
unassignFromEdge($event, item, false);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('asset.unassign-from-edge') },
icon: "portable_wifi_off",
isEnabled: function(asset) {
return asset && asset.edgeId && asset.edgeId.id !== types.id.nullUid;
}
}
);
assetActionsList.push(
{
onAction: function ($event, item) {
@ -240,6 +269,19 @@ export function AssetController($rootScope, userService, assetService, customerS
}
);
assetGroupActionsList.push(
{
onAction: function ($event, items) {
assignAssetsToEdge($event, items);
},
name: function() { return $translate.instant('asset.assign-assets') },
details: function(selectedCount) {
return $translate.instant('asset.assign-assets-text', {count: selectedCount}, "messageformat");
},
icon: "wifi_tethering"
}
);
assetGroupActionsList.push(
{
onAction: function ($event) {
@ -320,6 +362,51 @@ export function AssetController($rootScope, userService, assetService, customerS
}
vm.assetGridConfig.addItemActions = [];
} else if (vm.assetsScope === 'edge') {
fetchAssetsFunction = function (pageLink, assetType) {
return assetService.getEdgeAssets(edgeId, pageLink, null, assetType);
};
deleteAssetFunction = function (assetId) {
return assetService.unassignAssetFromEdge(assetId);
};
refreshAssetsParamsFunction = function () {
return {"edgeId": edgeId, "topIndex": vm.topIndex};
};
assetActionsList.push(
{
onAction: function ($event, item) {
unassignFromEdge($event, item, false);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('asset.unassign-from-edge') },
icon: "assignment_return"
}
);
assetGroupActionsList.push(
{
onAction: function ($event, items) {
unassignAssetsFromEdge($event, items);
},
name: function() { return $translate.instant('asset.unassign-assets') },
details: function(selectedCount) {
return $translate.instant('asset.unassign-assets-action-title', {count: selectedCount}, "messageformat");
},
icon: "assignment_return"
}
);
vm.assetGridConfig.addItemAction = {
onAction: function ($event) {
addAssetsToEdge($event);
},
name: function() { return $translate.instant('asset.assign-assets') },
details: function() { return $translate.instant('asset.assign-new-asset') },
icon: "add"
};
vm.assetGridConfig.addItemActions = [];
}
vm.assetGridConfig.refreshParamsFunc = refreshAssetsParamsFunction;
@ -531,4 +618,124 @@ export function AssetController($rootScope, userService, assetService, customerS
});
});
}
function assignToEdge($event, assetIds) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
edgeService.getEdges({limit: pageSize, textSearch: ''}).then(
function success(_edges) {
var edges = {
pageSize: pageSize,
data: _edges.data,
nextPageLink: _edges.nextPageLink,
selection: null,
hasNext: _edges.hasNext,
pending: false
};
if (edges.hasNext) {
edges.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AssignAssetToEdgeController',
controllerAs: 'vm',
templateUrl: assignToEdgeTemplate,
locals: {assetIds: assetIds, edges: edges},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function addAssetsToEdge($event) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
assetService.getTenantAssets({limit: pageSize, textSearch: ''}, false).then(
function success(_assets) {
var assets = {
pageSize: pageSize,
data: _assets.data,
nextPageLink: _assets.nextPageLink,
selections: {},
selectedCount: 0,
hasNext: _assets.hasNext,
pending: false
};
if (assets.hasNext) {
assets.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AddAssetsToEdgeController',
controllerAs: 'vm',
templateUrl: addAssetsToEdgeTemplate,
locals: {edgeId: edgeId, assets: assets},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function assignAssetsToEdge($event, items) {
var assetIds = [];
for (var id in items.selections) {
assetIds.push(id);
}
assignToEdge($event, assetIds);
}
function unassignFromEdge($event, asset) {
if ($event) {
$event.stopPropagation();
}
var title = $translate.instant('asset.unassign-asset-from-edge-title', {assetName: asset.name});
var content = $translate.instant('asset.unassign-asset-from-edge-text');
var label = $translate.instant('asset.unassign-asset');
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 () {
assetService.unassignAssetFromEdge(asset.id.id).then(function success() {
vm.grid.refreshList();
});
});
}
function unassignAssetsFromEdge($event, items) {
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title($translate.instant('asset.unassign-assets-title', {count: items.selectedCount}, 'messageformat'))
.htmlContent($translate.instant('asset.unassign-assets-text'))
.ariaLabel($translate.instant('asset.unassign-asset'))
.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(assetService.unassignAssetFromEdge(id));
}
$q.all(tasks).then(function () {
vm.grid.refreshList();
});
});
}
}

23
ui/src/app/asset/asset.routes.js

@ -67,6 +67,29 @@ export default function AssetRoutes($stateProvider, types) {
ncyBreadcrumb: {
label: '{"icon": "domain", "label": "{{ vm.customerAssetsTitle }}", "translate": "false"}'
}
})
.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"}'
}
});
}

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

@ -0,0 +1,123 @@
/*
* Copyright © 2016-2019 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 AssignAssetToEdgeController(edgeService, assetService, $mdDialog, $q, assetIds, edges) {
var vm = this;
vm.edges = edges;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.isEdgeSelected = isEdgeSelected;
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.getEdges(vm.edges.nextPageLink).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 i=0;i<assetIds.length;i++) {
tasks.push(assetService.assignAssetToEdge(vm.edges.selection.id.id, assetIds[i]));
}
$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();
if (vm.isEdgeSelected(edge)) {
vm.edges.selection = null;
} else {
vm.edges.selection = edge;
}
}
function isEdgeSelected(edge) {
return vm.edges.selection != null && edge &&
edge.id.id === vm.edges.selection.id.id;
}
function searchEdgeTextUpdated() {
vm.edges = {
pageSize: vm.edges.pageSize,
data: [],
nextPageLink: {
limit: vm.edges.pageSize,
textSearch: vm.searchText
},
selection: null,
hasNext: true,
pending: false
};
}
}

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

@ -0,0 +1,76 @@
<!--
Copyright © 2016-2019 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="{{ 'asset.assign-asset-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-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="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="vm.isEdgeSelected(edge)"></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.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>

4
ui/src/app/asset/index.js

@ -24,6 +24,8 @@ import {AssetController, AssetCardController} from './asset.controller';
import AssignAssetToCustomerController from './assign-to-customer.controller';
import AddAssetsToCustomerController from './add-assets-to-customer.controller';
import AssetDirective from './asset.directive';
import AssignAssetToEdgeController from './assign-to-edge.controller';
import AddAssetsToEdgeController from './add-assets-to-edge.controller';
export default angular.module('thingsboard.asset', [
uiRouter,
@ -37,5 +39,7 @@ export default angular.module('thingsboard.asset', [
.controller('AssetCardController', AssetCardController)
.controller('AssignAssetToCustomerController', AssignAssetToCustomerController)
.controller('AddAssetsToCustomerController', AddAssetsToCustomerController)
.controller('AssignAssetToEdgeController', AssignAssetToEdgeController)
.controller('AddAssetsToEdgeController', AddAssetsToEdgeController)
.directive('tbAsset', AssetDirective)
.name;

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

@ -0,0 +1,123 @@
/*
* Copyright © 2016-2019 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

@ -0,0 +1,77 @@
<!--
Copyright © 2016-2019 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="{{ 'device.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>

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

@ -0,0 +1,123 @@
/*
* Copyright © 2016-2019 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 AssignDeviceToEdgeController(edgeService, deviceService, $mdDialog, $q, deviceIds, edges) {
var vm = this;
vm.edges = edges;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.isEdgeSelected = isEdgeSelected;
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.getEdges(vm.edges.nextPageLink).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 i=0;i<deviceIds.length;i++) {
tasks.push(deviceService.assignDeviceToEdge(vm.edges.selection.id.id, deviceIds[i]));
}
$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();
if (vm.isEdgeSelected(edge)) {
vm.edges.selection = null;
} else {
vm.edges.selection = edge;
}
}
function isEdgeSelected(edge) {
return vm.edges.selection != null && edge &&
edge.id.id === vm.edges.selection.id.id;
}
function searchEdgeTextUpdated() {
vm.edges = {
pageSize: vm.edges.pageSize,
data: [],
nextPageLink: {
limit: vm.edges.pageSize,
textSearch: vm.searchText
},
selection: null,
hasNext: true,
pending: false
};
}
}

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

@ -0,0 +1,76 @@
<!--
Copyright © 2016-2019 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="{{ 'device.assign-device-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-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="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="vm.isEdgeSelected(edge)"></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.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>

211
ui/src/app/device/device.controller.js

@ -20,6 +20,8 @@ import deviceCard from './device-card.tpl.html';
import assignToCustomerTemplate from './assign-to-customer.tpl.html';
import addDevicesToCustomerTemplate from './add-devices-to-customer.tpl.html';
import deviceCredentialsTemplate from './device-credentials.tpl.html';
import assignToEdgeTemplate from './assign-to-edge.tpl.html';
import addDevicesToEdgeTemplate from './add-devices-to-edge.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
@ -48,10 +50,11 @@ export function DeviceCardController(types) {
/*@ngInject*/
export function DeviceController($rootScope, userService, deviceService, customerService, $state, $stateParams,
export function DeviceController($rootScope, userService, deviceService, customerService, edgeService, $state, $stateParams,
$document, $mdDialog, $q, $translate, types, importExport) {
var customerId = $stateParams.customerId;
var edgeId = $stateParams.edgeId;
var deviceActionsList = [];
@ -217,6 +220,35 @@ export function DeviceController($rootScope, userService, deviceService, custome
}
});
deviceActionsList.push(
{
onAction: function ($event, item) {
assignToEdge($event, [ item.id.id ]);
},
name: function() { return $translate.instant('action.assign') },
details: function() { return $translate.instant('device.assign-to-edge') },
icon: "wifi_tethering",
isEnabled: function(device) {
return device && (!device.edgeId || device.edgeId.id === types.id.nullUid);
}
}
);
deviceActionsList.push(
{
onAction: function ($event, item) {
unassignFromEdge($event, item, false);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('device.unassign-from-edge') },
icon: "portable_wifi_off",
isEnabled: function(device) {
return device && device.edgeId && device.edgeId.id !== types.id.nullUid;
}
}
);
deviceActionsList.push(
{
onAction: function ($event, item) {
@ -252,6 +284,20 @@ export function DeviceController($rootScope, userService, deviceService, custome
}
);
deviceGroupActionsList.push(
{
onAction: function ($event, items) {
assignDevicesToEdge($event, items);
},
name: function() { return $translate.instant('device.assign-devices') },
details: function(selectedCount) {
return $translate.instant('device.assign-devices-text', {count: selectedCount}, "messageformat");
},
icon: "wifi_tethering"
}
);
deviceGroupActionsList.push(
{
onAction: function ($event) {
@ -353,6 +399,50 @@ export function DeviceController($rootScope, userService, deviceService, custome
}
vm.deviceGridConfig.addItemActions = [];
} else if (vm.devicesScope === 'edge') {
fetchDevicesFunction = function (pageLink, deviceType) {
return deviceService.getEdgeDevices(edgeId, pageLink, null, deviceType);
};
deleteDeviceFunction = function (deviceId) {
return deviceService.unassignDeviceFromEdge(deviceId);
};
refreshDevicesParamsFunction = function () {
return {"edgeId": edgeId, "topIndex": vm.topIndex};
};
deviceActionsList.push(
{
onAction: function ($event, item) {
unassignFromEdge($event, item, false);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('device.unassign-from-edge') },
icon: "assignment_return"
}
);
deviceGroupActionsList.push(
{
onAction: function ($event, items) {
unassignDevicesFromEdge($event, items);
},
name: function() { return $translate.instant('device.unassign-devices') },
details: function(selectedCount) {
return $translate.instant('device.unassign-devices-action-title', {count: selectedCount}, "messageformat");
},
icon: "assignment_return"
}
);
vm.deviceGridConfig.addItemAction = {
onAction: function ($event) {
addDevicesToEdge($event);
},
name: function() { return $translate.instant('device.assign-devices') },
details: function() { return $translate.instant('device.assign-new-device') },
icon: "add"
};
vm.deviceGridConfig.addItemActions = [];
}
vm.deviceGridConfig.refreshParamsFunc = refreshDevicesParamsFunction;
@ -581,4 +671,123 @@ export function DeviceController($rootScope, userService, deviceService, custome
}, function () {
});
}
function assignToEdge($event, deviceIds) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
edgeService.getEdges({limit: pageSize, textSearch: ''}).then(
function success(_edges) {
var edges = {
pageSize: pageSize,
data: _edges.data,
nextPageLink: _edges.nextPageLink,
selection: null,
hasNext: _edges.hasNext,
pending: false
};
if (edges.hasNext) {
edges.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AssignDeviceToEdgeController',
controllerAs: 'vm',
templateUrl: assignToEdgeTemplate,
locals: {deviceIds: deviceIds, edges: edges},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function addDevicesToEdge($event) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
deviceService.getTenantDevices({limit: pageSize, textSearch: ''}, false).then(
function success(_devices) {
var devices = {
pageSize: pageSize,
data: _devices.data,
nextPageLink: _devices.nextPageLink,
selections: {},
selectedCount: 0,
hasNext: _devices.hasNext,
pending: false
};
if (devices.hasNext) {
devices.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AddDevicesToEdgeController',
controllerAs: 'vm',
templateUrl: addDevicesToEdgeTemplate,
locals: {edgeId: edgeId, devices: devices},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function assignDevicesToEdge($event, items) {
var deviceIds = [];
for (var id in items.selections) {
deviceIds.push(id);
}
assignToEdge($event, deviceIds);
}
function unassignFromEdge($event, device) {
if ($event) {
$event.stopPropagation();
}
var title = $translate.instant('device.unassign-device-from-edge-title', {deviceName: device.name});
var content = $translate.instant('device.unassign-device-from-edge-text');
var label = $translate.instant('device.unassign-device');
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 () {
deviceService.unassignDeviceFromEdge(device.id.id).then(function success() {
vm.grid.refreshList();
});
});
}
function unassignDevicesFromEdge($event, items) {
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title($translate.instant('device.unassign-devices-title', {count: items.selectedCount}, 'messageformat'))
.htmlContent($translate.instant('device.unassign-devices-text'))
.ariaLabel($translate.instant('device.unassign-device'))
.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(deviceService.unassignDeviceFromEdge(id));
}
$q.all(tasks).then(function () {
vm.grid.refreshList();
});
});
}
}

23
ui/src/app/device/device.routes.js

@ -67,6 +67,29 @@ export default function DeviceRoutes($stateProvider, types) {
ncyBreadcrumb: {
label: '{"icon": "devices_other", "label": "{{ vm.customerDevicesTitle }}", "translate": "false"}'
}
})
.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"}'
}
});
}

4
ui/src/app/device/index.js

@ -25,6 +25,8 @@ import AssignDeviceToCustomerController from './assign-to-customer.controller';
import AddDevicesToCustomerController from './add-devices-to-customer.controller';
import ManageDeviceCredentialsController from './device-credentials.controller';
import DeviceDirective from './device.directive';
import AssignDeviceToEdgeController from './assign-to-edge.controller';
import AddDevicesToEdgeController from './add-devices-to-edge.controller';
export default angular.module('thingsboard.device', [
uiRouter,
@ -39,5 +41,7 @@ export default angular.module('thingsboard.device', [
.controller('AssignDeviceToCustomerController', AssignDeviceToCustomerController)
.controller('AddDevicesToCustomerController', AddDevicesToCustomerController)
.controller('ManageDeviceCredentialsController', ManageDeviceCredentialsController)
.controller('AssignDeviceToEdgeController', AssignDeviceToEdgeController)
.controller('AddDevicesToEdgeController', AddDevicesToEdgeController)
.directive('tbDevice', DeviceDirective)
.name;

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

@ -216,6 +216,45 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
}
});
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) {
@ -573,4 +612,27 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
}
$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});
}
}

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

@ -0,0 +1,123 @@
/*
* Copyright © 2016-2019 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

@ -0,0 +1,77 @@
<!--
Copyright © 2016-2019 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="{{ 'entity-view.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>

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

@ -0,0 +1,123 @@
/*
* Copyright © 2016-2019 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 AssignEntityViewToEdgeController(edgeService, entityViewService, $mdDialog, $q, entityViewIds, edges) {
var vm = this;
vm.edges = edges;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.isEdgeSelected = isEdgeSelected;
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.getEdges(vm.edges.nextPageLink).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 i=0; i < entityViewIds.length;i++) {
tasks.push(entityViewService.assignEntityViewToEdge(vm.edges.selection.id.id, entityViewIds[i]));
}
$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();
if (vm.isEdgeSelected(edge)) {
vm.edges.selection = null;
} else {
vm.edges.selection = edge;
}
}
function isEdgeSelected(edge) {
return vm.edges.selection != null && edge &&
edge.id.id === vm.edges.selection.id.id;
}
function searchEdgeTextUpdated() {
vm.edges = {
pageSize: vm.edges.pageSize,
data: [],
nextPageLink: {
limit: vm.edges.pageSize,
textSearch: vm.searchText
},
selection: null,
hasNext: true,
pending: false
};
}
}

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

@ -0,0 +1,76 @@
<!--
Copyright © 2016-2019 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="{{ 'entity-view.assign-entity-view-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-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="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="vm.isEdgeSelected(edge)"></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.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>

208
ui/src/app/entity-view/entity-view.controller.js

@ -19,6 +19,8 @@ import addEntityViewTemplate from './add-entity-view.tpl.html';
import entityViewCard from './entity-view-card.tpl.html';
import assignToCustomerTemplate from './assign-to-customer.tpl.html';
import addEntityViewsToCustomerTemplate from './add-entity-views-to-customer.tpl.html';
import assignToEdgeTemplate from './assign-to-edge.tpl.html';
import addEntityViewsToEdgeTemplate from './add-entity-views-to-edge.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
@ -47,10 +49,11 @@ export function EntityViewCardController(types) {
/*@ngInject*/
export function EntityViewController($rootScope, userService, entityViewService, customerService, $state, $stateParams,
export function EntityViewController($rootScope, userService, entityViewService, customerService, edgeService, $state, $stateParams,
$document, $mdDialog, $q, $translate, types) {
var customerId = $stateParams.customerId;
var edgeId = $stateParams.edgeId;
var entityViewActionsList = [];
@ -192,6 +195,32 @@ export function EntityViewController($rootScope, userService, entityViewService,
}
});
entityViewActionsList.push({
onAction: function ($event, item) {
assignToEdge($event, [ item.id.id ]);
},
name: function() { return $translate.instant('action.assign') },
details: function() { return $translate.instant('entity-view.assign-to-edge') },
icon: "wifi_tethering",
isEnabled: function(entityView) {
return entityView && (!entityView.edgeId || entityView.edgeId.id === types.id.nullUid);
}
}
);
entityViewActionsList.push({
onAction: function ($event, item) {
unassignFromEdge($event, item, false);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('entity-view.unassign-from-edge') },
icon: "portable_wifi_off",
isEnabled: function(entityView) {
return entityView && entityView.edgeId && entityView.edgeId.id !== types.id.nullUid;
}
}
);
entityViewActionsList.push(
{
onAction: function ($event, item) {
@ -216,6 +245,19 @@ export function EntityViewController($rootScope, userService, entityViewService,
}
);
entityViewGroupActionsList.push(
{
onAction: function ($event, items) {
assignEntityViewsToEdge($event, items);
},
name: function() { return $translate.instant('entity-view.assign-assets') },
details: function(selectedCount) {
return $translate.instant('entity-view.assign-entity-views-text', {count: selectedCount}, "messageformat");
},
icon: "wifi_tethering"
}
);
entityViewGroupActionsList.push(
{
onAction: function ($event) {
@ -281,6 +323,51 @@ export function EntityViewController($rootScope, userService, entityViewService,
} else if (vm.entityViewsScope === 'customer_user') {
vm.entityViewGridConfig.addItemAction = {};
}
} else if (vm.entityViewsScope === 'edge') {
fetchEntityViewsFunction = function (pageLink, entityViewType) {
return entityViewService.getEdgeEntityViews(edgeId, pageLink, null, entityViewType);
};
deleteEntityViewFunction = function (entityViewId) {
return entityViewService.unassignEntityViewFromEdge(entityViewId);
};
refreshEntityViewsParamsFunction = function () {
return {"edgeId": edgeId, "topIndex": vm.topIndex};
};
entityViewActionsList.push(
{
onAction: function ($event, item) {
unassignFromEdge($event, item, false);
},
name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('entity-view.unassign-from-edge') },
icon: "assignment_return"
}
);
entityViewGroupActionsList.push(
{
onAction: function ($event, items) {
unassignEntityViewsFromEdge($event, items);
},
name: function() { return $translate.instant('entity-view.unassign-entity-views') },
details: function(selectedCount) {
return $translate.instant('entity-view.unassign-entity-views-action-title', {count: selectedCount}, "messageformat");
},
icon: "assignment_return"
}
);
vm.entityViewGridConfig.addItemAction = {
onAction: function ($event) {
addEntityViewsToEdge($event);
},
name: function() { return $translate.instant('entity-view.assign-entity-views') },
details: function() { return $translate.instant('entity-view.assign-new-entity-view') },
icon: "add"
};
vm.entityViewGridConfig.addItemActions = [];
}
vm.entityViewGridConfig.refreshParamsFunc = refreshEntityViewsParamsFunction;
@ -492,4 +579,123 @@ export function EntityViewController($rootScope, userService, entityViewService,
});
});
}
function assignToEdge($event, entityViewIds) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
edgeService.getEdges({limit: pageSize, textSearch: ''}).then(
function success(_edges) {
var edges = {
pageSize: pageSize,
data: _edges.data,
nextPageLink: _edges.nextPageLink,
selection: null,
hasNext: _edges.hasNext,
pending: false
};
if (edges.hasNext) {
edges.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AssignEntityViewToEdgeController',
controllerAs: 'vm',
templateUrl: assignToEdgeTemplate,
locals: {entityViewIds: entityViewIds, edges: edges},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function addEntityViewsToEdge($event) {
if ($event) {
$event.stopPropagation();
}
var pageSize = 10;
entityViewService.getTenantEntityViews({limit: pageSize, textSearch: ''}, false).then(
function success(_entityViews) {
var entityViews = {
pageSize: pageSize,
data: _entityViews.data,
nextPageLink: _entityViews.nextPageLink,
selections: {},
selectedCount: 0,
hasNext: _entityViews.hasNext,
pending: false
};
if (entityViews.hasNext) {
entityViews.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AddEntityViewsToEdgeController',
controllerAs: 'vm',
templateUrl: addEntityViewsToEdgeTemplate,
locals: {edgeId: edgeId, entityViews: entityViews},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
}
function assignEntityViewsToEdge($event, items) {
var entityViewIds = [];
for (var id in items.selections) {
entityViewIds.push(id);
}
assignToEdge($event, entityViewIds);
}
function unassignFromEdge($event, entityView) {
if ($event) {
$event.stopPropagation();
}
var title = $translate.instant('entity-view.unassign-entity-view-from-edge-title', {entityViewName: entityView.name});
var content = $translate.instant('entity-view.unassign-entity-view-from-edge-text');
var label = $translate.instant('entity-view.unassign-entity-view');
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 () {
entityViewService.unassignEntityViewFromEdge(entityView.id.id).then(function success() {
vm.grid.refreshList();
});
});
}
function unassignEntityViewsFromEdge($event, items) {
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title($translate.instant('entity-view.unassign-entity-views-title', {count: items.selectedCount}, 'messageformat'))
.htmlContent($translate.instant('entity-view.unassign-entity-views-text'))
.ariaLabel($translate.instant('entity-view.unassign-entity-view'))
.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(entityViewService.unassignEntityViewFromEdge(id));
}
$q.all(tasks).then(function () {
vm.grid.refreshList();
});
});
}
}

23
ui/src/app/entity-view/entity-view.routes.js

@ -67,6 +67,29 @@ export default function EntityViewRoutes($stateProvider, types) {
ncyBreadcrumb: {
label: '{"icon": "view_quilt", "label": "{{ vm.customerEntityViewsTitle }}", "translate": "false"}'
}
})
.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"}'
}
});
}

4
ui/src/app/entity-view/index.js

@ -24,6 +24,8 @@ import {EntityViewController, EntityViewCardController} from './entity-view.cont
import AssignEntityViewToCustomerController from './assign-to-customer.controller';
import AddEntityViewsToCustomerController from './add-entity-views-to-customer.controller';
import EntityViewDirective from './entity-view.directive';
import AssignEntityViewToEdgeController from './assign-to-edge.controller';
import AddEntityViewsToEdgeController from './add-entity-views-to-edge.controller';
export default angular.module('thingsboard.entityView', [
uiRouter,
@ -37,5 +39,7 @@ export default angular.module('thingsboard.entityView', [
.controller('EntityViewCardController', EntityViewCardController)
.controller('AssignEntityViewToCustomerController', AssignEntityViewToCustomerController)
.controller('AddEntityViewsToCustomerController', AddEntityViewsToCustomerController)
.controller('AssignEntityViewToEdgeController', AssignEntityViewToEdgeController)
.controller('AddEntityViewsToEdgeController', AddEntityViewsToEdgeController)
.directive('tbEntityView', EntityViewDirective)
.name;

35
ui/src/app/locale/locale.constant-en_US.json

@ -271,7 +271,14 @@
"asset-required": "Asset is required",
"name-starts-with": "Asset name starts with",
"import": "Import assets",
"asset-file": "Asset file"
"asset-file": "Asset file",
"assign-asset-to-edge": "Assign Asset(s) To Edge",
"assign-asset-to-edge-text":"Please select the assets to assign to the edge",
"unassign-from-edge": "Unassign from edge",
"assign-to-edge": "Assign to edge",
"assign-to-edge-text": "Please select the edge to assign the asset(s)",
"unassign-asset-from-edge-title": "Are you sure you want to unassign the asset '{{assetName}}'?",
"unassign-asset-from-edge-text": "After the confirmation the asset will be unassigned and won't be accessible by the edge."
},
"attribute": {
"attributes": "Attributes",
@ -716,7 +723,14 @@
"device-public": "Device is public",
"select-device": "Select device",
"import": "Import device",
"device-file": "Device file"
"device-file": "Device file",
"assign-device-to-edge": "Assign Device(s) To Edge",
"assign-device-to-edge-text":"Please select the devices to assign to the edge",
"unassign-from-edge": "Unassign from edge",
"assign-to-edge": "Assign to edge",
"assign-to-edge-text": "Please select the edge to assign the device(s)",
"unassign-device-from-edge-title": "Are you sure you want to unassign the device '{{deviceName}}'?",
"unassign-device-from-edge-text": "After the confirmation the device will be unassigned and won't be accessible by the edge."
},
"dialog": {
"close": "Close dialog"
@ -783,7 +797,13 @@
"edge-key-copied-message": "Edge key has been copied to clipboard",
"edge-secret": "Edge secret",
"copy-edge-secret": "Copy edge secret",
"edge-secret-copied-message": "Edge secret has been copied to clipboard"
"edge-secret-copied-message": "Edge secret has been copied to clipboard",
"manage-edge-assets": "Manage edge assets",
"manage-edge-devices": "Manage edge devices",
"manage-edge-entity-views": "Manage edge entity views",
"assets": "Edge assets",
"devices": "Edge devices",
"entity-views": "Edge entity views"
},
"error": {
"unable-to-connect": "Unable to connect to the server! Please check your internet connection.",
@ -986,7 +1006,14 @@
"make-public-entity-view-title": "Are you sure you want to make the entity view '{{entityViewName}}' public?",
"make-public-entity-view-text": "After the confirmation the entity view and all its data will be made public and accessible by others.",
"make-private-entity-view-title": "Are you sure you want to make the entity view '{{entityViewName}}' private?",
"make-private-entity-view-text": "After the confirmation the entity view and all its data will be made private and won't be accessible by others."
"make-private-entity-view-text": "After the confirmation the entity view and all its data will be made private and won't be accessible by others.",
"assign-entity-view-to-edge": "Assign Entity View(s) To Edge",
"assign-entity-view-to-edge-text":"Please select the entity views to assign to the edge",
"unassign-from-edge": "Unassign from edge",
"assign-to-edge": "Assign to edge",
"assign-to-edge-text": "Please select the edge to assign the entity view(s)",
"unassign-entity-view-from-edge-title": "Are you sure you want to unassign the entity view '{{entityViewName}}'?",
"unassign-entity-view-from-edge-text": "After the confirmation the entity view will be unassigned and won't be accessible by the edge."
},
"event": {
"event-type": "Event type",

Loading…
Cancel
Save