Commit 201476f912e317ce7d7c466c0bffec72189611bb

Authored by nickAS21
Committed by Andrew Shvayka
1 parent 3420eeb9

lwm2m: add objectKeyId to back and front profile

Showing 26 changed files with 458 additions and 140 deletions
... ... @@ -27,11 +27,14 @@ import org.thingsboard.server.common.data.Resource;
27 27 import org.thingsboard.server.common.data.ResourceType;
28 28 import org.thingsboard.server.common.data.exception.ThingsboardException;
29 29 import org.thingsboard.server.common.data.id.TenantId;
  30 +import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
30 31 import org.thingsboard.server.common.data.page.PageData;
31 32 import org.thingsboard.server.common.data.page.PageLink;
32 33 import org.thingsboard.server.dao.resource.ResourceService;
33 34 import org.thingsboard.server.queue.util.TbCoreComponent;
34 35
  36 +import java.util.List;
  37 +
35 38 @Slf4j
36 39 @RestController
37 40 @TbCoreComponent
... ... @@ -59,21 +62,55 @@ public class ResourceController extends BaseController {
59 62 }
60 63
61 64 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
62   - @RequestMapping(value = "/resource", method = RequestMethod.GET)
  65 + @RequestMapping(value = "/resource/page", method = RequestMethod.GET)
63 66 @ResponseBody
64 67 public PageData<Resource> getResources(@RequestParam(required = false) boolean system,
65 68 @RequestParam int pageSize,
66 69 @RequestParam int page,
  70 + @RequestParam(required = false) String textSearch,
67 71 @RequestParam(required = false) String sortProperty,
68 72 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
69 73 try {
70   - PageLink pageLink = createPageLink(pageSize, page, null, sortProperty, sortOrder);
  74 +// int[] objectIds;
  75 +// ResourceType resourceType
  76 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
71 77 return checkNotNull(resourceService.findResourcesByTenantId(system ? TenantId.SYS_TENANT_ID : getTenantId(), pageLink));
72 78 } catch (Exception e) {
73 79 throw handleException(e);
74 80 }
75 81 }
76 82
  83 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  84 + @RequestMapping(value = "/resource/lwm2m/page", method = RequestMethod.GET)
  85 + @ResponseBody
  86 + public List<LwM2mObject> getLwm2mListObjectsPage(@RequestParam int pageSize,
  87 + @RequestParam int page,
  88 + @RequestParam(required = false) String textSearch,
  89 + @RequestParam(required = false) String sortProperty,
  90 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  91 + try {
  92 + PageLink pageLink = new PageLink(pageSize, page, textSearch);
  93 + return checkNotNull(resourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink));
  94 + } catch (Exception e) {
  95 + throw handleException(e);
  96 + }
  97 + }
  98 +
  99 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  100 + @RequestMapping(value = "/resource/lwm2m", method = RequestMethod.GET)
  101 + @ResponseBody
  102 + public List<LwM2mObject> getLwm2mListObjects(@RequestParam String sortOrder,
  103 + @RequestParam String sortProperty,
  104 + @RequestParam(required = false) String[] objectIds,
  105 + @RequestParam(required = false) String searchText)
  106 + throws ThingsboardException {
  107 + try {
  108 + return checkNotNull(resourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds, searchText));
  109 + } catch (Exception e) {
  110 + throw handleException(e);
  111 + }
  112 + }
  113 +
77 114 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
78 115 @RequestMapping(value = "/resource/{resourceType}/{resourceId}", method = RequestMethod.DELETE)
79 116 @ResponseBody
... ...
... ... @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
37 37 import org.thingsboard.server.common.data.widget.WidgetType;
38 38 import org.thingsboard.server.common.data.widget.WidgetsBundle;
39 39 import org.thingsboard.server.dao.dashboard.DashboardService;
  40 +import org.thingsboard.server.dao.exception.DataValidationException;
40 41 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
41 42 import org.thingsboard.server.dao.resource.ResourceService;
42 43 import org.thingsboard.server.dao.rule.RuleChainService;
... ... @@ -202,7 +203,6 @@ public class InstallScripts {
202 203 }
203 204
204 205 public void loadSystemLwm2mResources() throws Exception {
205   -// Path modelsDir = Paths.get("/home/nick/Igor_project/thingsboard_ce_3_2_docker/thingsboard/common/transport/lwm2m/src/main/resources/models/");
206 206 Path modelsDir = Paths.get(getDataDir(), MODELS_DIR);
207 207 if (Files.isDirectory(modelsDir)) {
208 208 try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(modelsDir, path -> path.toString().endsWith(XML_EXT))) {
... ... @@ -210,18 +210,16 @@ public class InstallScripts {
210 210 path -> {
211 211 try {
212 212 byte[] fileBytes = Files.readAllBytes(path);
213   - String key = getObjectModelLwm2mValid(fileBytes, path.getFileName().toString(), new DefaultDDFFileValidator());
214   - if (key != null) {
215   - Resource resource = new Resource();
216   - resource.setTenantId(TenantId.SYS_TENANT_ID);
217   - resource.setResourceType(ResourceType.LWM2M_MODEL);
218   - resource.setResourceId(key);
219   - resource.setValue(Base64.getEncoder().encodeToString(fileBytes));
220   - resourceService.saveResource(resource);
221   - }
  213 + String source = new String(fileBytes);
  214 + Resource resource = new Resource();
  215 + resource.setTenantId(TenantId.SYS_TENANT_ID);
  216 + resource.setResourceType(ResourceType.LWM2M_MODEL);
  217 + resource.setResourceId(getValueByTeg(source, "ObjectID") + "_" + getValueByTeg(source, "ObjectVersion"));
  218 + resource.setTextSearch(resource.getResourceId() + ":" + getValueByTeg(source, "Name"));
  219 + resource.setValue(Base64.getEncoder().encodeToString(fileBytes));
  220 + resourceService.saveResource(resource);
222 221 } catch (Exception e) {
223   - log.error("Unable to load lwm2m model [{}]", path.toString());
224   - throw new RuntimeException("Unable to load lwm2m model", e);
  222 + throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", path.toString()));
225 223 }
226 224 }
227 225 );
... ... @@ -234,6 +232,7 @@ public class InstallScripts {
234 232 resource.setTenantId(TenantId.SYS_TENANT_ID);
235 233 resource.setResourceType(ResourceType.JKS);
236 234 resource.setResourceId(jksPath.getFileName().toString());
  235 + resource.setTextSearch(jksPath.getFileName().toString());
237 236 resource.setValue(Base64.getEncoder().encodeToString(Files.readAllBytes(jksPath)));
238 237 resourceService.saveResource(resource);
239 238 } catch (Exception e) {
... ... @@ -242,11 +241,18 @@ public class InstallScripts {
242 241 }
243 242 }
244 243
245   - private String getObjectModelLwm2mValid(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) {
  244 + private String getValueByTeg(String source, String tagHtml) {
  245 + int lenTag = ("<" + tagHtml + ">").length();
  246 + int indStart = source.indexOf("<" + tagHtml + ">");
  247 + int indEnd = source.indexOf("</" + tagHtml + ">");
  248 + return (indStart > 0 && indEnd > 0) ? source.substring(indStart + lenTag, indEnd) : null;
  249 +
  250 + }
  251 +
  252 + private ObjectModel getObjectModelLwm2mValid(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) {
246 253 try {
247 254 DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator);
248   - ObjectModel objectModel = ddfFileParser.parseEx(new ByteArrayInputStream(xmlByte), streamName).get(0);
249   - return objectModel.id + "##" + objectModel.getVersion();
  255 + return ddfFileParser.parseEx(new ByteArrayInputStream(xmlByte), streamName).get(0);
250 256 } catch (IOException | InvalidDDFFileException e) {
251 257 log.error("Could not parse the XML file [{}]", streamName, e);
252 258 return null;
... ...
... ... @@ -442,6 +442,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
442 442 " tenant_id uuid NOT NULL," +
443 443 " resource_type varchar(32) NOT NULL," +
444 444 " resource_id varchar(255) NOT NULL," +
  445 + " text_search varchar(255)," +
445 446 " resource_value varchar," +
446 447 " CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)" +
447 448 " );");
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.resource;
18 18 import org.thingsboard.server.common.data.Resource;
19 19 import org.thingsboard.server.common.data.ResourceType;
20 20 import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
21 22 import org.thingsboard.server.common.data.page.PageData;
22 23 import org.thingsboard.server.common.data.page.PageLink;
23 24
... ... @@ -31,7 +32,18 @@ public interface ResourceService {
31 32
32 33 PageData<Resource> findResourcesByTenantId(TenantId tenantId, PageLink pageLink);
33 34
34   - List<Resource> findResourcesByTenantIdResourceType(TenantId tenantId, ResourceType resourceType);
  35 + List<LwM2mObject> findLwM2mObject(TenantId tenantId,
  36 + String sortOrder,
  37 + String sortProperty,
  38 + String[] objectIds,
  39 + String searchText);
  40 +
  41 + List<LwM2mObject> findLwM2mObjectPage(TenantId tenantId,
  42 + String sortProperty,
  43 + String sortOrder,
  44 + PageLink pageLink);
  45 +
  46 + List<Resource> findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType);
35 47
36 48 void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId);
37 49
... ...
... ... @@ -79,6 +79,10 @@
79 79 <groupId>org.thingsboard</groupId>
80 80 <artifactId>protobuf-dynamic</artifactId>
81 81 </dependency>
  82 + <dependency>
  83 + <groupId>org.eclipse.leshan</groupId>
  84 + <artifactId>leshan-core</artifactId>
  85 + </dependency>
82 86 </dependencies>
83 87
84 88 <build>
... ...
... ... @@ -16,10 +16,24 @@
16 16 package org.thingsboard.server.common.data;
17 17
18 18 import lombok.Data;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.leshan.core.model.DDFFileParser;
  21 +import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
  22 +import org.eclipse.leshan.core.model.InvalidDDFFileException;
  23 +import org.eclipse.leshan.core.model.ObjectModel;
19 24 import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.common.data.lwm2m.LwM2mInstance;
  26 +import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
  27 +import org.thingsboard.server.common.data.lwm2m.LwM2mResource;
20 28
  29 +import java.io.ByteArrayInputStream;
  30 +import java.io.IOException;
21 31 import java.io.Serializable;
  32 +import java.util.ArrayList;
  33 +import java.util.Base64;
  34 +import java.util.List;
22 35
  36 +@Slf4j
23 37 @Data
24 38 public class Resource implements HasTenantId, Serializable {
25 39
... ... @@ -28,14 +42,51 @@ public class Resource implements HasTenantId, Serializable {
28 42 private TenantId tenantId;
29 43 private ResourceType resourceType;
30 44 private String resourceId;
  45 + private String textSearch;
31 46 private String value;
32 47
33 48 @Override
34 49 public String toString() {
35   - return "Resource{" +
36   - "tenantId=" + tenantId +
37   - ", resourceType=" + resourceType +
38   - ", resourceId='" + resourceId + '\'' +
39   - '}';
  50 + final StringBuilder res = new StringBuilder("Resource{");
  51 + res.append("tenantId=").append(tenantId);
  52 + res.append(", resourceType='").append(resourceType).append('\'');
  53 + res.append(", resourceId='").append(resourceId).append('\'');
  54 + res.append(", textSearch='").append(textSearch).append('\'');
  55 + res.append('}');
  56 + return res.toString();
  57 + }
  58 +
  59 + public LwM2mObject toLwM2mObject () {
  60 + try {
  61 + DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator());
  62 + List<ObjectModel> objectModels = ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(this.value)), this.textSearch);
  63 + if (objectModels.size() == 0) {
  64 + return null;
  65 + }
  66 + else {
  67 + ObjectModel obj = objectModels.get(0);
  68 + LwM2mObject lwM2mObject = new LwM2mObject();
  69 + lwM2mObject.setId(obj.id);
  70 + lwM2mObject.setKeyId(this.resourceId);
  71 + lwM2mObject.setName(obj.name);
  72 + lwM2mObject.setMultiple(obj.multiple);
  73 + lwM2mObject.setMandatory(obj.mandatory);
  74 + LwM2mInstance instance = new LwM2mInstance();
  75 + instance.setId(0);
  76 + List<LwM2mResource> resources = new ArrayList<>();
  77 + obj.resources.forEach((k, v) -> {
  78 + if (!v.operations.isExecutable()) {
  79 + LwM2mResource resource = new LwM2mResource(k, v.name, false, false, false);
  80 + resources.add(resource);
  81 + }
  82 + });
  83 + instance.setResources(resources.stream().toArray(LwM2mResource[]::new));
  84 + lwM2mObject.setInstances(new LwM2mInstance[]{instance});
  85 + return lwM2mObject;
  86 + }
  87 + } catch (IOException | InvalidDDFFileException e) {
  88 + log.error("Could not parse the XML of objectModel with name [{}]", this.textSearch, e);
  89 + return null;
  90 + }
40 91 }
41 92 }
... ...
... ... @@ -20,6 +20,7 @@ import lombok.Data;
20 20 @Data
21 21 public class LwM2mObject {
22 22 int id;
  23 + String keyId;
23 24 String name;
24 25 boolean multiple;
25 26 boolean mandatory;
... ...
... ... @@ -214,6 +214,10 @@
214 214 <groupId>org.elasticsearch.client</groupId>
215 215 <artifactId>rest</artifactId>
216 216 </dependency>
  217 + <dependency>
  218 + <groupId>org.eclipse.leshan</groupId>
  219 + <artifactId>leshan-core</artifactId>
  220 + </dependency>
217 221 </dependencies>
218 222 <build>
219 223 <plugins>
... ...
... ... @@ -460,8 +460,10 @@ public class ModelConstants {
460 460 public static final String RESOURCE_TENANT_ID_COLUMN = TENANT_ID_COLUMN;
461 461 public static final String RESOURCE_TYPE_COLUMN = "resource_type";
462 462 public static final String RESOURCE_ID_COLUMN = "resource_id";
  463 + public static final String TEXT_SEARCH_COLUMN = "text_search";
463 464 public static final String RESOURCE_VALUE_COLUMN = "resource_value";
464 465
  466 +
465 467 /**
466 468 * Cassandra attributes and timeseries constants.
467 469 */
... ...
... ... @@ -33,6 +33,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAM
33 33 import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN;
34 34 import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TYPE_COLUMN;
35 35 import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_VALUE_COLUMN;
  36 +import static org.thingsboard.server.dao.model.ModelConstants.TEXT_SEARCH_COLUMN;
36 37
37 38 @Data
38 39 @Entity
... ... @@ -52,6 +53,9 @@ public class ResourceEntity implements ToData<Resource> {
52 53 @Column(name = RESOURCE_ID_COLUMN)
53 54 private String resourceId;
54 55
  56 + @Column(name = TEXT_SEARCH_COLUMN)
  57 + private String textSearch;
  58 +
55 59 @Column(name = RESOURCE_VALUE_COLUMN)
56 60 private String value;
57 61
... ... @@ -62,6 +66,7 @@ public class ResourceEntity implements ToData<Resource> {
62 66 this.tenantId = resource.getTenantId().getId();
63 67 this.resourceType = resource.getResourceType().name();
64 68 this.resourceId = resource.getResourceId();
  69 + this.textSearch = resource.getTextSearch();
65 70 this.value = resource.getValue();
66 71 }
67 72
... ... @@ -71,6 +76,7 @@ public class ResourceEntity implements ToData<Resource> {
71 76 resource.setTenantId(new TenantId(tenantId));
72 77 resource.setResourceType(ResourceType.valueOf(resourceType));
73 78 resource.setResourceId(resourceId);
  79 + resource.setTextSearch(textSearch);
74 80 resource.setValue(value);
75 81 return resource;
76 82 }
... ...
... ... @@ -16,15 +16,28 @@
16 16 package org.thingsboard.server.dao.resource;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.leshan.core.model.DDFFileParser;
  20 +import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
  21 +import org.eclipse.leshan.core.model.InvalidDDFFileException;
  22 +import org.eclipse.leshan.core.model.ObjectModel;
19 23 import org.springframework.stereotype.Service;
20 24 import org.thingsboard.server.common.data.Resource;
21 25 import org.thingsboard.server.common.data.ResourceType;
22 26 import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.data.lwm2m.LwM2mInstance;
  28 +import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
  29 +import org.thingsboard.server.common.data.lwm2m.LwM2mResource;
23 30 import org.thingsboard.server.common.data.page.PageData;
24 31 import org.thingsboard.server.common.data.page.PageLink;
25 32 import org.thingsboard.server.dao.exception.DataValidationException;
26 33
  34 +import java.io.ByteArrayInputStream;
  35 +import java.io.IOException;
  36 +import java.util.ArrayList;
  37 +import java.util.Base64;
  38 +import java.util.Comparator;
27 39 import java.util.List;
  40 +import java.util.stream.Collectors;
28 41
29 42 import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID;
30 43 import static org.thingsboard.server.dao.service.Validator.validateId;
... ... @@ -69,10 +82,35 @@ public class BaseResourceService implements ResourceService {
69 82
70 83
71 84 @Override
72   - public List<Resource> findResourcesByTenantIdResourceType(TenantId tenantId, ResourceType resourceType) {
  85 + public List<Resource> findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType) {
73 86 log.trace("Executing findByTenantId [{}]", tenantId);
74 87 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
75   - return resourceDao.findAllByTenantIdResourceType(tenantId, resourceType);
  88 + return resourceDao.findAllByTenantIdAndResourceType(tenantId, resourceType);
  89 + }
  90 +
  91 + @Override
  92 + public List<LwM2mObject> findLwM2mObjectPage(TenantId tenantId, String sortProperty, String sortOrder, PageLink pageLink) {
  93 + log.trace("Executing findByTenantId [{}]", tenantId);
  94 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  95 + PageData<Resource> resourcePageData = resourceDao.findResourcesByTenantIdAndResourceType(
  96 + tenantId,
  97 + ResourceType.LWM2M_MODEL, pageLink);
  98 + List<LwM2mObject> lwM2mObjects = resourcePageData.getData().stream().map(this::toLwM2mObject).collect(Collectors.toList());
  99 + return lwM2mObjects.size() > 1 ? this.sortList (lwM2mObjects, sortProperty, sortOrder) : lwM2mObjects;
  100 + }
  101 +
  102 + @Override
  103 + public List<LwM2mObject> findLwM2mObject(TenantId tenantId, String sortOrder,
  104 + String sortProperty,
  105 + String[] objectIds,
  106 + String searchText) {
  107 + log.trace("Executing findByTenantId [{}]", tenantId);
  108 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  109 + List<Resource> resources = resourceDao.findResourcesByTenantIdAndResourceType(tenantId, ResourceType.LWM2M_MODEL,
  110 + objectIds,
  111 + searchText);
  112 + List<LwM2mObject> lwM2mObjects = resources.stream().map(this::toLwM2mObject).collect(Collectors.toList());
  113 + return lwM2mObjects.size() > 1 ? this.sortList (lwM2mObjects, sortProperty, sortOrder) : lwM2mObjects;
76 114 }
77 115
78 116 @Override
... ... @@ -86,11 +124,13 @@ public class BaseResourceService implements ResourceService {
86 124 if (resource == null) {
87 125 throw new DataValidationException("Resource should be specified!");
88 126 }
89   -
90 127 if (resource.getValue() == null) {
91 128 throw new DataValidationException("Resource value should be specified!");
92 129 }
93 130 validate(resource.getTenantId(), resource.getResourceType(), resource.getResourceId());
  131 + if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && resource.toLwM2mObject() == null) {
  132 + throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getTextSearch()));
  133 + }
94 134 }
95 135
96 136 protected void validate(TenantId tenantId, ResourceType resourceType, String resourceId) {
... ... @@ -103,4 +143,61 @@ public class BaseResourceService implements ResourceService {
103 143 validateId(tenantId, "Incorrect tenantId ");
104 144 }
105 145
  146 + private LwM2mObject toLwM2mObject(Resource resource) {
  147 + try {
  148 + DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator());
  149 + List<ObjectModel> objectModels =
  150 + ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getValue())), resource.getTextSearch());
  151 + if (objectModels.size() == 0) {
  152 + return null;
  153 + } else {
  154 + ObjectModel obj = objectModels.get(0);
  155 + LwM2mObject lwM2mObject = new LwM2mObject();
  156 + lwM2mObject.setId(obj.id);
  157 + lwM2mObject.setKeyId(resource.getResourceId());
  158 + lwM2mObject.setName(obj.name);
  159 + lwM2mObject.setMultiple(obj.multiple);
  160 + lwM2mObject.setMandatory(obj.mandatory);
  161 + LwM2mInstance instance = new LwM2mInstance();
  162 + instance.setId(0);
  163 + List<LwM2mResource> resources = new ArrayList<>();
  164 + obj.resources.forEach((k, v) -> {
  165 + if (!v.operations.isExecutable()) {
  166 + LwM2mResource lwM2mResource = new LwM2mResource(k, v.name, false, false, false);
  167 + resources.add(lwM2mResource);
  168 + }
  169 + });
  170 + instance.setResources(resources.stream().toArray(LwM2mResource[]::new));
  171 + lwM2mObject.setInstances(new LwM2mInstance[]{instance});
  172 + return lwM2mObject;
  173 + }
  174 + } catch (IOException | InvalidDDFFileException e) {
  175 + log.error("Could not parse the XML of objectModel with name [{}]", resource.getTextSearch(), e);
  176 + return null;
  177 + }
  178 + }
  179 +
  180 + private List<LwM2mObject> sortList (List<LwM2mObject> lwM2mObjects, String sortProperty, String sortOrder) {
  181 + switch (sortProperty) {
  182 + case "name":
  183 + switch (sortOrder) {
  184 + case "ASC":
  185 + lwM2mObjects.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
  186 + break;
  187 + case "DESC":
  188 + lwM2mObjects.stream().sorted(Comparator.comparing(LwM2mObject::getName).reversed());
  189 + break;
  190 + }
  191 + case "id":
  192 + switch (sortOrder) {
  193 + case "ASC":
  194 + lwM2mObjects.sort((o1, o2) -> Long.compare(o1.getId(), o2.getId()));
  195 + break;
  196 + case "DESC":
  197 + lwM2mObjects.sort((o1, o2) -> Long.compare(o2.getId(), o1.getId()));
  198 + }
  199 + }
  200 + return lwM2mObjects;
  201 + }
  202 +
106 203 }
... ...
... ... @@ -33,8 +33,15 @@ public interface ResourceDao {
33 33
34 34 PageData<Resource> findAllByTenantId(TenantId tenantId, PageLink pageLink);
35 35
  36 + PageData<Resource> findResourcesByTenantIdAndResourceType(TenantId tenantId,
  37 + ResourceType resourceType,
  38 + PageLink pageLink);
36 39
37   - List<Resource> findAllByTenantIdResourceType(TenantId tenantId, ResourceType resourceType);
  40 + List<Resource> findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType);
38 41
  42 + List<Resource> findResourcesByTenantIdAndResourceType(TenantId tenantId,
  43 + ResourceType resourceType,
  44 + String[] objectIds,
  45 + String searchText);
39 46 void removeAllByTenantId(TenantId tenantId);
40 47 }
... ...
... ... @@ -29,6 +29,7 @@ import org.thingsboard.server.dao.model.sql.ResourceEntity;
29 29 import org.thingsboard.server.dao.resource.ResourceDao;
30 30
31 31 import java.util.List;
  32 +import java.util.Objects;
32 33
33 34 @Slf4j
34 35 @Component
... ... @@ -73,11 +74,40 @@ public class ResourceDaoImpl implements ResourceDao {
73 74 }
74 75
75 76 @Override
76   - public List<Resource> findAllByTenantIdResourceType(TenantId tenantId, ResourceType resourceType) {
  77 + public List<Resource> findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType) {
77 78 return DaoUtil.convertDataList(resourceRepository.findAllByTenantIdAndResourceType(tenantId.getId(), resourceType.name()));
78 79 }
79 80
80 81 @Override
  82 + public PageData<Resource> findResourcesByTenantIdAndResourceType(TenantId tenantId,
  83 + ResourceType resourceType,
  84 + PageLink pageLink) {
  85 + return DaoUtil.toPageData(resourceRepository.findResourcesPage(
  86 + tenantId.getId(),
  87 + TenantId.SYS_TENANT_ID.getId(),
  88 + resourceType.name(),
  89 + Objects.toString(pageLink.getTextSearch(), ""),
  90 + DaoUtil.toPageable(pageLink)
  91 + ));
  92 + }
  93 +
  94 + @Override
  95 + public List<Resource> findResourcesByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType,
  96 + String[] objectIds,
  97 + String searchText) {
  98 + return objectIds == null ?
  99 + DaoUtil.convertDataList(resourceRepository.findResources(
  100 + tenantId.getId(),
  101 + TenantId.SYS_TENANT_ID.getId(),
  102 + resourceType.name(),
  103 + Objects.toString(searchText, ""))) :
  104 + DaoUtil.convertDataList(resourceRepository.findResourcesByIds(
  105 + tenantId.getId(),
  106 + TenantId.SYS_TENANT_ID.getId(),
  107 + resourceType.name(), objectIds));
  108 + }
  109 +
  110 + @Override
81 111 public void removeAllByTenantId(TenantId tenantId) {
82 112 resourceRepository.removeAllByTenantId(tenantId.getId());
83 113 }
... ...
... ... @@ -17,7 +17,9 @@ package org.thingsboard.server.dao.sql.resource;
17 17
18 18 import org.springframework.data.domain.Page;
19 19 import org.springframework.data.domain.Pageable;
  20 +import org.springframework.data.jpa.repository.Query;
20 21 import org.springframework.data.repository.CrudRepository;
  22 +import org.springframework.data.repository.query.Param;
21 23 import org.thingsboard.server.dao.model.sql.ResourceCompositeKey;
22 24 import org.thingsboard.server.dao.model.sql.ResourceEntity;
23 25
... ... @@ -26,10 +28,57 @@ import java.util.UUID;
26 28
27 29 public interface ResourceRepository extends CrudRepository<ResourceEntity, ResourceCompositeKey> {
28 30
  31 +
29 32 Page<ResourceEntity> findAllByTenantId(UUID tenantId, Pageable pageable);
30 33
  34 + @Query("SELECT tr FROM ResourceEntity tr " +
  35 + "WHERE tr.resourceType = :resourceType " +
  36 + "AND LOWER(tr.textSearch) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
  37 + "AND (tr.tenantId = :tenantId " +
  38 + "OR (tr.tenantId = :systemAdminId " +
  39 + "AND NOT EXISTS " +
  40 + "(SELECT sr FROM ResourceEntity sr " +
  41 + "WHERE sr.tenantId = :tenantId " +
  42 + "AND sr.resourceType = :resourceType " +
  43 + "AND tr.resourceId = sr.resourceId)))")
  44 + Page<ResourceEntity> findResourcesPage(
  45 + @Param("tenantId") UUID tenantId,
  46 + @Param("systemAdminId") UUID sysAdminId,
  47 + @Param("resourceType") String resourceType,
  48 + @Param("searchText") String search,
  49 + Pageable pageable);
31 50
32 51 List<ResourceEntity> findAllByTenantIdAndResourceType(UUID tenantId, String resourceType);
33 52
34 53 void removeAllByTenantId(UUID tenantId);
  54 +
  55 + @Query("SELECT tr FROM ResourceEntity tr " +
  56 + "WHERE tr.resourceType = :resourceType " +
  57 + "AND LOWER(tr.textSearch) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
  58 + "AND (tr.tenantId = :tenantId " +
  59 + "OR (tr.tenantId = :systemAdminId " +
  60 + "AND NOT EXISTS " +
  61 + "(SELECT sr FROM ResourceEntity sr " +
  62 + "WHERE sr.tenantId = :tenantId " +
  63 + "AND sr.resourceType = :resourceType " +
  64 + "AND tr.resourceId = sr.resourceId)))")
  65 + List<ResourceEntity> findResources(@Param("tenantId") UUID tenantId,
  66 + @Param("systemAdminId") UUID sysAdminId,
  67 + @Param("resourceType") String resourceType,
  68 + @Param("searchText") String search);
  69 +
  70 + @Query("SELECT tr FROM ResourceEntity tr " +
  71 + "WHERE tr.resourceType = :resourceType " +
  72 + "AND tr.resourceId in (:resourceIds) " +
  73 + "AND (tr.tenantId = :tenantId " +
  74 + "OR (tr.tenantId = :systemAdminId " +
  75 + "AND NOT EXISTS " +
  76 + "(SELECT sr FROM ResourceEntity sr " +
  77 + "WHERE sr.tenantId = :tenantId " +
  78 + "AND sr.resourceType = :resourceType " +
  79 + "AND tr.resourceId = sr.resourceId)))")
  80 + List<ResourceEntity> findResourcesByIds(@Param("tenantId") UUID tenantId,
  81 + @Param("systemAdminId") UUID sysAdminId,
  82 + @Param("resourceType") String resourceType,
  83 + @Param("resourceIds") String[] objectIds);
35 84 }
... ...
... ... @@ -425,6 +425,7 @@ CREATE TABLE IF NOT EXISTS resource (
425 425 tenant_id uuid NOT NULL,
426 426 resource_type varchar(32) NOT NULL,
427 427 resource_id varchar(255) NOT NULL,
  428 + text_search varchar(255),
428 429 resource_value varchar,
429 430 CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)
430 431 );
... ...
... ... @@ -451,6 +451,7 @@ CREATE TABLE IF NOT EXISTS resource (
451 451 tenant_id uuid NOT NULL,
452 452 resource_type varchar(32) NOT NULL,
453 453 resource_id varchar(255) NOT NULL,
  454 + text_search varchar(255),
454 455 resource_value varchar,
455 456 CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)
456 457 );
... ...
... ... @@ -14,16 +14,16 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { Injectable } from '@angular/core';
18   -import { HttpClient } from '@angular/common/http';
19   -import { PageLink } from '@shared/models/page/page-link';
20   -import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
21   -import { Observable } from 'rxjs';
22   -import { PageData } from '@shared/models/page/page-data';
23   -import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models';
24   -import { isDefinedAndNotNull, isEmptyStr } from '@core/utils';
25   -import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/profile-config.models';
26   -import { SortOrder } from '@shared/models/page/sort-order';
  17 +import {Injectable} from '@angular/core';
  18 +import {HttpClient} from '@angular/common/http';
  19 +import {PageLink} from '@shared/models/page/page-link';
  20 +import {defaultHttpOptionsFromConfig, RequestConfig} from './http-utils';
  21 +import {Observable} from 'rxjs';
  22 +import {PageData} from '@shared/models/page/page-data';
  23 +import {DeviceProfile, DeviceProfileInfo, DeviceTransportType} from '@shared/models/device.models';
  24 +import {isDefinedAndNotNull, isEmptyStr} from '@core/utils';
  25 +import {ObjectLwM2M, ServerSecurityConfig} from '@home/components/profile/device/lwm2m/profile-config.models';
  26 +import {SortOrder} from '@shared/models/page/sort-order';
27 27
28 28 @Injectable({
29 29 providedIn: 'root'
... ... @@ -43,9 +43,9 @@ export class DeviceProfileService {
43 43 return this.http.get<DeviceProfile>(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config));
44 44 }
45 45
46   - public getLwm2mObjects(sortOrder: SortOrder, objectIds?: number[], searchText?: string, config?: RequestConfig):
  46 + public getLwm2mObjects(sortOrder: SortOrder, objectIds?: string[], searchText?: string, config?: RequestConfig):
47 47 Observable<Array<ObjectLwM2M>> {
48   - let url = `/api/lwm2m/deviceProfile/?sortProperty=${sortOrder.property}&sortOrder=${sortOrder.direction}`;
  48 + let url = `/api/resource/lwm2m/?sortProperty=${sortOrder.property}&sortOrder=${sortOrder.direction}`;
49 49 if (isDefinedAndNotNull(objectIds) && objectIds.length > 0) {
50 50 url += `&objectIds=${objectIds}`;
51 51 }
... ... @@ -63,9 +63,9 @@ export class DeviceProfileService {
63 63 );
64 64 }
65 65
66   - public getLwm2mObjectsPage(pageLink: PageLink, config?: RequestConfig): Observable<PageData<ObjectLwM2M>> {
67   - return this.http.get<PageData<ObjectLwM2M>>(
68   - `/api/lwm2m/deviceProfile/objects${pageLink.toQuery()}`,
  66 + public getLwm2mObjectsPage(pageLink: PageLink, config?: RequestConfig): Observable<Array<ObjectLwM2M>> {
  67 + return this.http.get<Array<ObjectLwM2M>>(
  68 + `/api/resource/lwm2m/page${pageLink.toQuery()}`,
69 69 defaultHttpOptionsFromConfig(config)
70 70 );
71 71 }
... ...
... ... @@ -172,7 +172,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
172 172
173 173 private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => {
174 174 this.lwm2mDeviceProfileFormGroup.patchValue({
175   - observeAttrTelemetry: this.getObserveAttrTelemetryObjects(objectsList)
  175 + observeAttrTelemetry: deepClone(this.getObserveAttrTelemetryObjects(objectsList))
176 176 },
177 177 {emitEvent: false});
178 178 }
... ... @@ -195,32 +195,32 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
195 195 }
196 196 }
197 197
198   - private getObserveAttrTelemetryObjects = (listObject: ObjectLwM2M[]): object => {
199   - const clientObserveAttrTelemetry = listObject;
200   - if (this.configurationValue.observeAttr) {
  198 + private getObserveAttrTelemetryObjects = (objectList: ObjectLwM2M[]): object => {
  199 + const objectLwM2MS = deepClone(objectList);
  200 + if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) {
201 201 const observeArray = this.configurationValue.observeAttr.observe;
202 202 const attributeArray = this.configurationValue.observeAttr.attribute;
203 203 const telemetryArray = this.configurationValue.observeAttr.telemetry;
204 204 let keyNameJson = this.configurationValue.observeAttr.keyName;
205 205 if (this.includesNotZeroInstance(attributeArray, telemetryArray)) {
206   - this.addInstances(attributeArray, telemetryArray, clientObserveAttrTelemetry);
  206 + this.addInstances(attributeArray, telemetryArray, objectLwM2MS);
207 207 }
208 208 if (isDefinedAndNotNull(observeArray)) {
209   - this.updateObserveAttrTelemetryObjects(observeArray, clientObserveAttrTelemetry, OBSERVE);
  209 + this.updateObserveAttrTelemetryObjects(observeArray, objectLwM2MS, OBSERVE);
210 210 }
211 211 if (isDefinedAndNotNull(attributeArray)) {
212   - this.updateObserveAttrTelemetryObjects(attributeArray, clientObserveAttrTelemetry, ATTRIBUTE);
  212 + this.updateObserveAttrTelemetryObjects(attributeArray, objectLwM2MS, ATTRIBUTE);
213 213 }
214 214 if (isDefinedAndNotNull(telemetryArray)) {
215   - this.updateObserveAttrTelemetryObjects(telemetryArray, clientObserveAttrTelemetry, TELEMETRY);
  215 + this.updateObserveAttrTelemetryObjects(telemetryArray, objectLwM2MS, TELEMETRY);
216 216 }
217 217 if (isDefinedAndNotNull(keyNameJson)) {
218 218 this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray);
219 219 this.upDateJsonAllConfig();
220   - this.updateKeyNameObjects(clientObserveAttrTelemetry);
  220 + this.updateKeyNameObjects(objectLwM2MS);
221 221 }
222 222 }
223   - return {clientLwM2M: clientObserveAttrTelemetry};
  223 + return {clientLwM2M: objectLwM2MS};
224 224 }
225 225
226 226 private includesNotZeroInstance = (attribute: string[], telemetry: string[]): boolean => {
... ... @@ -235,35 +235,39 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
235 235 .sort(this.sortPath);
236 236
237 237 new Set(instancesPath).forEach(path => {
238   - const pathParameter = Array.from(path.split('/'), Number);
239   - const objectLwM2M = clientObserveAttrTelemetry.find(x => x.id === pathParameter[0]);
  238 + const pathParameter = Array.from(path.split('/'), String);
  239 + const objectLwM2M = clientObserveAttrTelemetry.find(x => x.keyId === pathParameter[0]);
240 240 if (objectLwM2M) {
241 241 const instance = deepClone(objectLwM2M.instances[0]);
242   - instance.id = pathParameter[1];
  242 + instance.id = +pathParameter[1];
243 243 objectLwM2M.instances.push(instance);
244 244 }
245 245 });
246 246 }
247 247
248   - private updateObserveAttrTelemetryObjects = (parameters: string[], clientObserveAttrTelemetry: ObjectLwM2M[],
  248 + private updateObserveAttrTelemetryObjects = (parameters: string[], objectLwM2MS: ObjectLwM2M[],
249 249 nameParameter: string): void => {
250 250 parameters.forEach(parameter => {
251   - const [objectId, instanceId, resourceId] = Array.from(parameter.substring(1).split('/'), Number);
252   - clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.id === objectId)
253   - .instances.find(itrInstance => itrInstance.id === instanceId)
254   - .resources.find(resource => resource.id === resourceId)
255   - [nameParameter] = true;
  251 + const [objectKeyId, instanceId, resourceId] = Array.from(parameter.substring(1).split('/'), String);
  252 + const objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId);
  253 + if (objectLwM2M) {
  254 + objectLwM2M.instances.find(itrInstance => itrInstance.id === +instanceId)
  255 + .resources.find(resource => resource.id === +resourceId)
  256 + [nameParameter] = true;
  257 + }
256 258 });
  259 +
257 260 }
258 261
259 262 private updateKeyNameObjects = (clientObserveAttrTelemetry: ObjectLwM2M[]): void => {
260 263 Object.keys(this.configurationValue.observeAttr.keyName).forEach(key => {
261   - const [objectId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), Number);
262   - clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.id === objectId)
263   - .instances.find(instance => instance.id === instanceId)
264   - .resources.find(resource => resource.id === resourceId)
  264 + const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String);
  265 + const objectLwM2M = clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.keyId === objectKeyId)
  266 + if (objectLwM2M) {
  267 + objectLwM2M.instances.find(instance => instance.id === +instanceId)
  268 + .resources.find(resource => resource.id === +resourceId)
265 269 .keyName = this.configurationValue.observeAttr.keyName[key];
266   - });
  270 + }});
267 271 }
268 272
269 273 private validateKeyNameObjects = (nameJson: JsonObject, attributeArray: JsonArray, telemetryArray: JsonArray): {} => {
... ... @@ -290,7 +294,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
290 294 if (instance.hasOwnProperty(RESOURCES) && Array.isArray(instance.resources)) {
291 295 instance.resources.forEach(resource => {
292 296 if (resource.attribute || resource.telemetry) {
293   - let pathRes = `/${obj.id}/${instance.id}/${resource.id}`;
  297 + let pathRes = `/${obj.keyId}/${instance.id}/${resource.id}`;
294 298 if (resource.observe) {
295 299 observeArray.push(pathRes);
296 300 }
... ... @@ -345,22 +349,22 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
345 349 });
346 350 }
347 351
348   - private getObjectsFromJsonAllConfig = (): number[] => {
349   - const objectsIds = new Set<number>();
  352 + private getObjectsFromJsonAllConfig = (): string[] => {
  353 + const objectsIds = new Set<string>();
350 354 if (this.configurationValue.observeAttr) {
351 355 if (this.configurationValue.observeAttr.observe) {
352 356 this.configurationValue.observeAttr.observe.forEach(obj => {
353   - objectsIds.add(Array.from(obj.substring(1).split('/'), Number)[0]);
  357 + objectsIds.add(Array.from(obj.substring(1).split('/'), String)[0]);
354 358 });
355 359 }
356 360 if (this.configurationValue.observeAttr.attribute) {
357 361 this.configurationValue.observeAttr.attribute.forEach(obj => {
358   - objectsIds.add(Array.from(obj.substring(1).split('/'), Number)[0]);
  362 + objectsIds.add(Array.from(obj.substring(1).split('/'), String)[0]);
359 363 });
360 364 }
361 365 if (this.configurationValue.observeAttr.telemetry) {
362 366 this.configurationValue.observeAttr.telemetry.forEach(obj => {
363   - objectsIds.add(Array.from(obj.substring(1).split('/'), Number)[0]);
  367 + objectsIds.add(Array.from(obj.substring(1).split('/'), String)[0]);
364 368 });
365 369 }
366 370 }
... ... @@ -379,21 +383,21 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
379 383
380 384 removeObjectsList = (value: ObjectLwM2M): void => {
381 385 const objectsOld = this.lwm2mDeviceProfileFormGroup.get(OBSERVE_ATTR_TELEMETRY).value.clientLwM2M;
382   - const isIdIndex = (element) => element.id === value.id;
  386 + const isIdIndex = (element) => element.keyId === value.keyId;
383 387 const index = objectsOld.findIndex(isIdIndex);
384 388 if (index >= 0) {
385 389 objectsOld.splice(index, 1);
386 390 }
387   - this.removeObserveAttrTelemetryFromJson(OBSERVE, value.id);
388   - this.removeObserveAttrTelemetryFromJson(TELEMETRY, value.id);
389   - this.removeObserveAttrTelemetryFromJson(ATTRIBUTE, value.id);
390   - this.removeKeyNameFromJson(value.id);
  391 + this.removeObserveAttrTelemetryFromJson(OBSERVE, value.keyId);
  392 + this.removeObserveAttrTelemetryFromJson(TELEMETRY, value.keyId);
  393 + this.removeObserveAttrTelemetryFromJson(ATTRIBUTE, value.keyId);
  394 + this.removeKeyNameFromJson(value.keyId);
391 395 this.updateObserveAttrTelemetryObjectFormGroup(objectsOld);
392 396 this.upDateJsonAllConfig();
393 397 }
394 398
395   - private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, id: number): void => {
396   - const isIdIndex = (element) => element.startsWith(`/${id}`);
  399 + private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, keyId: string): void => {
  400 + const isIdIndex = (element) => element.startsWith(`/${keyId}`);
397 401 let index = this.configurationValue.observeAttr[observeAttrTel].findIndex(isIdIndex);
398 402 while (index >= 0) {
399 403 this.configurationValue.observeAttr[observeAttrTel].splice(index, 1);
... ... @@ -401,10 +405,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
401 405 }
402 406 }
403 407
404   - private removeKeyNameFromJson = (id: number): void => {
  408 + private removeKeyNameFromJson = (keyId: string): void => {
405 409 const keyNameJson = this.configurationValue.observeAttr.keyName;
406 410 Object.keys(keyNameJson).forEach(key => {
407   - if (key.startsWith(`/${id}`)) {
  411 + if (key.startsWith(`/${keyId}`)) {
408 412 delete keyNameJson[key];
409 413 }
410 414 });
... ...
... ... @@ -14,12 +14,11 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { Component, forwardRef, } from '@angular/core';
18   -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
19   -import { Store } from '@ngrx/store';
20   -import { AppState } from '@core/core.state';
21   -import { INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER } from './profile-config.models';
22   -import { DeviceProfileService } from '@core/http/device-profile.service';
  17 +import {Component, forwardRef,} from '@angular/core';
  18 +import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
  19 +import {Store} from '@ngrx/store';
  20 +import {AppState} from '@core/core.state';
  21 +import {INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER} from './profile-config.models';
23 22
24 23 @Component({
25 24 selector: 'tb-profile-lwm2m-object-add-instances-list',
... ... @@ -43,7 +42,6 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso
43 42 private propagateChange = (v: any) => { };
44 43
45 44 constructor(private store: Store<AppState>,
46   - private deviceProfileService: DeviceProfileService,
47 45 private fb: FormBuilder) {
48 46 this.instancesListFormGroup = this.fb.group({
49 47 instanceIdInput: [null, [
... ...
... ... @@ -17,7 +17,7 @@
17 17 -->
18 18 <form [formGroup]="instancesFormGroup" (ngSubmit)="add()" style="min-width: 400px;">
19 19 <mat-toolbar fxLayout="row" color="primary">
20   - <b><i>{{data.objectName}}</i></b> &nbsp;&nbsp; (object&nbsp;[<b>{{data.objectId}}</b>])
  20 + <b><i>{{data.objectName}}</i></b> &nbsp;&nbsp; (object&nbsp;[<b>{{data.objectKeyId}}</b>])
21 21 <span fxFlex></span>
22 22 <button mat-button mat-icon-button
23 23 (click)="cancel()"
... ...
... ... @@ -14,18 +14,18 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { Component, Inject, OnInit } from '@angular/core';
18   -import { DialogComponent } from '@shared/components/dialog.component';
19   -import { FormBuilder, FormGroup } from '@angular/forms';
20   -import { Store } from '@ngrx/store';
21   -import { AppState } from '@core/core.state';
22   -import { Router } from '@angular/router';
23   -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
  17 +import {Component, Inject, OnInit} from '@angular/core';
  18 +import {DialogComponent} from '@shared/components/dialog.component';
  19 +import {FormBuilder, FormGroup} from '@angular/forms';
  20 +import {Store} from '@ngrx/store';
  21 +import {AppState} from '@core/core.state';
  22 +import {Router} from '@angular/router';
  23 +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
24 24
25 25 export interface Lwm2mObjectAddInstancesData {
26 26 instancesIds: Set<number>;
27 27 objectName?: string;
28   - objectId?: number;
  28 + objectKeyId?: string;
29 29 }
30 30
31 31 @Component({
... ...
... ... @@ -40,7 +40,7 @@
40 40 class="tb-autocomplete"
41 41 [displayWith]="displayObjectLwm2mFn">
42 42 <mat-option *ngFor="let objectLwm2m of filteredObjectsList | async" [value]="objectLwm2m">
43   - <span [innerHTML]="objectLwm2m.id + ': ' + objectLwm2m.name | highlight:searchText"></span>
  43 + <span [innerHTML]="objectLwm2m.keyId + ': ' + objectLwm2m.name | highlight:searchText"></span>
44 44 </mat-option>
45 45 <mat-option *ngIf="!(filteredObjectsList | async)?.length" [value]="null">
46 46 <span>
... ...
... ... @@ -14,17 +14,18 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
18   -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
19   -import { coerceBooleanProperty } from '@angular/cdk/coercion';
20   -import { Store } from '@ngrx/store';
21   -import { AppState } from '@core/core.state';
22   -import { Observable } from 'rxjs';
23   -import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
24   -import { ModelValue, ObjectLwM2M } from './profile-config.models';
25   -import { DeviceProfileService } from '@core/http/device-profile.service';
26   -import { Direction } from '@shared/models/page/sort-order';
27   -import { isDefined, isDefinedAndNotNull, isEmptyStr, isString } from '@core/utils';
  17 +import {Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild} from '@angular/core';
  18 +import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
  19 +import {coerceBooleanProperty} from '@angular/cdk/coercion';
  20 +import {Store} from '@ngrx/store';
  21 +import {AppState} from '@core/core.state';
  22 +import {Observable} from 'rxjs';
  23 +import {filter, map, mergeMap, publishReplay, refCount, tap} from 'rxjs/operators';
  24 +import {ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT} from './profile-config.models';
  25 +import {DeviceProfileService} from '@core/http/device-profile.service';
  26 +import {Direction} from '@shared/models/page/sort-order';
  27 +import {isDefined, isDefinedAndNotNull, isString} from '@core/utils';
  28 +import {PageLink} from "@shared/models/page/page-link";
28 29
29 30 @Component({
30 31 selector: 'tb-profile-lwm2m-object-list',
... ... @@ -41,7 +42,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
41 42 private requiredValue: boolean;
42 43 private dirty = false;
43 44 private lw2mModels: Observable<Array<ObjectLwM2M>>;
44   - private modelValue: Array<number> = [];
  45 + private modelValue: Array<string> = [];
45 46
46 47 lwm2mListFormGroup: FormGroup;
47 48 objectsList: Array<ObjectLwM2M> = [];
... ... @@ -134,8 +135,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
134 135 }
135 136
136 137 private add(object: ObjectLwM2M): void {
137   - if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.id) === -1) {
138   - this.modelValue.push(object.id);
  138 + if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.keyId) === -1) {
  139 + this.modelValue.push(object.keyId);
139 140 this.objectsList.push(object);
140 141 this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList);
141 142 this.addList.next(this.objectsList);
... ... @@ -148,7 +149,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
148 149 if (index >= 0) {
149 150 this.objectsList.splice(index, 1);
150 151 this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList);
151   - index = this.modelValue.indexOf(object.id);
  152 + index = this.modelValue.indexOf(object.keyId);
152 153 this.modelValue.splice(index, 1);
153 154 this.removeList.next(object);
154 155 this.clear();
... ... @@ -159,25 +160,29 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
159 160 return object ? object.name : undefined;
160 161 }
161 162
162   - private fetchListObjects = (searchText?: string): Observable<Array<ObjectLwM2M>> => {
  163 + private fetchListObjects = (searchText?: string): Observable<Array<ObjectLwM2M>> => {
163 164 this.searchText = searchText;
164   - const filters = {names: [], ids: []};
165   - if (isDefinedAndNotNull(searchText) && !isEmptyStr(searchText)) {
166   - const ids = searchText.match(/\d+/g);
167   - filters.ids = ids !== null ? ids.map(Number) : filters.ids;
168   - filters.names = searchText.trim().toUpperCase().split(' ');
169   - }
170   - const predicate = objectLwM2M => filters.names.find(word => objectLwM2M.name.toUpperCase().includes(word))
171   - || filters.ids.includes(objectLwM2M.id);
172   - return this.getLwM2mModels().pipe(
173   - map(objectLwM2Ms => searchText ? objectLwM2Ms.filter(predicate) : objectLwM2Ms)
174   - );
  165 + return this.getLwM2mModelsPage().pipe(
  166 + map(objectLwM2Ms => objectLwM2Ms)
  167 + );
  168 + }
  169 +
  170 + private getLwM2mModelsPage(): Observable<Array<ObjectLwM2M>> {
  171 + const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, {
  172 + property: 'id',
  173 + direction: Direction.ASC
  174 + });
  175 + this.lw2mModels = this.deviceProfileService.getLwm2mObjectsPage(pageLink).pipe(
  176 + publishReplay(1),
  177 + refCount()
  178 + );
  179 + return this.lw2mModels;
175 180 }
176 181
177 182 private getLwM2mModels(): Observable<Array<ObjectLwM2M>> {
178 183 if (!this.lw2mModels) {
179 184 const sortOrder = {
180   - property: 'name',
  185 + property: 'id',
181 186 direction: Direction.ASC
182 187 };
183 188 this.lw2mModels = this.deviceProfileService.getLwm2mObjects(sortOrder).pipe(
... ...
... ... @@ -22,7 +22,7 @@
22 22 [formGroupName]="i">
23 23 <mat-expansion-panel-header>
24 24 <mat-panel-title fxLayoutAlign="start center">
25   - <b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;id: {{ objectLwM2M.get('id').value}}>
  25 + <b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;id: {{ objectLwM2M.get('keyId').value}}>
26 26 </mat-panel-title>
27 27 <mat-panel-description fxLayoutAlign="end center" *ngIf="!disabled">
28 28 <button type="button"
... ...
... ... @@ -15,7 +15,7 @@
15 15 ///
16 16
17 17
18   -import { Component, forwardRef, Input } from '@angular/core';
  18 +import {Component, forwardRef, Input} from '@angular/core';
19 19 import {
20 20 AbstractControl,
21 21 ControlValueAccessor,
... ... @@ -25,13 +25,13 @@ import {
25 25 NG_VALUE_ACCESSOR,
26 26 Validators
27 27 } from '@angular/forms';
28   -import { Store } from '@ngrx/store';
29   -import { AppState } from '@core/core.state';
30   -import { coerceBooleanProperty } from '@angular/cdk/coercion';
31   -import { CLIENT_LWM2M, Instance, INSTANCES, ObjectLwM2M, ResourceLwM2M, RESOURCES } from './profile-config.models';
32   -import { deepClone, isDefinedAndNotNull, isEqual, isUndefined } from '@core/utils';
33   -import { MatDialog } from '@angular/material/dialog';
34   -import { TranslateService } from '@ngx-translate/core';
  28 +import {Store} from '@ngrx/store';
  29 +import {AppState} from '@core/core.state';
  30 +import {coerceBooleanProperty} from '@angular/cdk/coercion';
  31 +import {CLIENT_LWM2M, Instance, INSTANCES, ObjectLwM2M, ResourceLwM2M, RESOURCES} from './profile-config.models';
  32 +import {deepClone, isDefinedAndNotNull, isEqual, isUndefined} from '@core/utils';
  33 +import {MatDialog} from '@angular/material/dialog';
  34 +import {TranslateService} from '@ngx-translate/core';
35 35 import {
36 36 Lwm2mObjectAddInstancesComponent,
37 37 Lwm2mObjectAddInstancesData
... ... @@ -139,7 +139,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
139 139 private createObjectsLwM2M = (objectsLwM2M: ObjectLwM2M[]): FormArray => {
140 140 return this.fb.array(objectsLwM2M.map((objectLwM2M) => {
141 141 return this.fb.group({
142   - id: objectLwM2M.id,
  142 + keyId: objectLwM2M.keyId,
143 143 name: objectLwM2M.name,
144 144 multiple: objectLwM2M.multiple,
145 145 mandatory: objectLwM2M.mandatory,
... ... @@ -228,7 +228,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
228 228 data: {
229 229 instancesIds: this.instancesToSetId(object.instances),
230 230 objectName: object.name,
231   - objectId: object.id
  231 + objectKeyId: object.keyId
232 232 }
233 233 }).afterClosed().subscribe(
234 234 (res: Lwm2mObjectAddInstancesData | undefined) => {
... ... @@ -241,7 +241,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
241 241
242 242 private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => {
243 243 const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls
244   - .find(e => e.value.id === data.objectId) as FormGroup;
  244 + .find(e => e.value.keyId === data.objectKeyId) as FormGroup;
245 245 const instancesArray = objectLwM2MFormGroup.value.instances as Instance [];
246 246 const instancesFormArray = objectLwM2MFormGroup.get(INSTANCES) as FormArray;
247 247 const instance0 = deepClone(instancesFormArray.at(0).value as Instance);
... ...
... ... @@ -14,6 +14,7 @@
14 14 /// limitations under the License.
15 15 ///
16 16
  17 +export const PAGE_SIZE_LIMIT = 50;
17 18 export const INSTANCES = 'instances';
18 19 export const RESOURCES = 'resources';
19 20 export const CLIENT_LWM2M = 'clientLwM2M';
... ... @@ -58,7 +59,7 @@ export const SECURITY_CONFIG_MODE_NAMES = new Map<SECURITY_CONFIG_MODE, string>(
58 59 );
59 60
60 61 export interface ModelValue {
61   - objectIds: number[] | null,
  62 + objectIds: string[] | null,
62 63 objectsList: ObjectLwM2M[]
63 64 }
64 65
... ... @@ -190,6 +191,7 @@ export interface Instance {
190 191 */
191 192 export interface ObjectLwM2M {
192 193 id: number;
  194 + keyId: string;
193 195 name: string;
194 196 multiple?: boolean;
195 197 mandatory?: boolean;
... ...